Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

File watcher module #628

Merged
merged 2 commits into from
Feb 12, 2017
Merged

File watcher module #628

merged 2 commits into from
Feb 12, 2017

Conversation

jknack
Copy link
Member

@jknack jknack commented Feb 7, 2017

file watcher

Watches for file system changes or event. It uses a watch service to monitor a directory for changes so that it can update its display of the list of files when files are created or deleted.

dependency

<dependency>
 <groupId>org.jooby</groupId>
 <artifactId>jooby-file watcher</artifactId>
 <version>{{version}}</version>
</dependency>

usage

Watch mydir and listen for create, modify or delete event on all files under it:

{
  use(new FileWatcher()
    .register(Paths.get("mydir"), (kind, path) -> {
      log.info("found {} on {}", kind, path);
    });
  );
}

file handler

You can specify a {@link FileEventHandler} instance or class while registering a path:

Instance:

{
  use(new FileWatcher()
    .register(Paths.get("mydir"), (kind, path) -> {
      log.info("found {} on {}", kind, path);
    });
  );
}

Class reference:

{
  use(new FileWatcher()
    .register(Paths.get("mydir"), MyFileEventHandler.class)
  );
}

Worth to mention that MyFileEventHandler will be provided by Guice.

options

You can specify a couple of options at registration time:

{
  use(new FileWatcher(
    .register(Paths.get("mydir"), MyFileEventHandler.class, options -> {
      options.kind(StandardWatchEventKinds.ENTRY_MODIFY)
             .recursive(false)
             .includes("*.java");
    });
  ));
}
  1. Here we listen for {@link StandardWatchEventKinds#ENTRY_MODIFY} (we don't care about create or delete).

  2. We turn off recursive watching, only direct files are detected.

  3. We want .java files and we ignore any other files.

configuration file

In addition to do it programmatically, you can do it via configuration properties:

{
  use(new FileWatcher()
    .register("mydirproperty", MyEventHandler.class)
  );
}

The mydirproperty property must be present in your .conf file.

But of course, you can entirely register paths from .conf file:

filewatcher {
  register {
    path: "mydir"
    handler: "org.example.MyFileEventHandler"
    kind: "ENTRY_MODIFY"
    includes: "*.java"
    recursive: false
  }
}

Multiple paths are supported using array notation:

filewatcher {
  register: [{
    path: "mydir1"
    handler: "org.example.MyFileEventHandler"
  }, {
    path: "mydir2"
    handler: "org.example.MyFileEventHandler"
  }]
}

Now use the module:

{
  use(new FileWatcher());
}

The FileWatcher module read the filewatcher.register property and setup everything.

@jknack jknack added the feature label Feb 7, 2017
@jknack jknack added this to the 1.1.0 milestone Feb 7, 2017
@krisztiankocsis
Copy link
Member

Hi, I can do this today near 20:00 CET.
Are there any specific requests or things should I consider?

@jknack
Copy link
Member Author

jknack commented Feb 7, 2017

Not at all, just a new module :) which I probably going to reuse in the rocker template module.

Thanks.

@jknack
Copy link
Member Author

jknack commented Feb 7, 2017

Sorry, I got now what you mean. I didn't wrote anything about it yet, but for now let's make sure to use 2 spaces for indent, one statement for line, final for parameters.

I'm open to suggestion too.


@Override
public String toString() {
return "**/*.*";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it should be "**/*" since there might be files without '.'

}
};

private static final WatchEvent.Kind<?>[] KINDS = {ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY };
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs a space after the opening bracket.

}

FileEventHandler handler(
final Function<Class<? extends FileEventHandler>, FileEventHandler> factory) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4 spaces used for indent instead of 2.
Is this an exception when breaking long lines in method parameters?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, max line characters is 100, when we couldn't fit everything in one line, we use 4 spaces.

class FileMonitor implements Runnable {

/** The logging system. */
private final Logger log = LoggerFactory.getLogger(getClass());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this not static?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Class is a singleton, no need to use a static logger. Also, I read somewhere that static logger cause leaks within dynamic class loaders (like the one we use with jooby:run), so unless is strictly necessary I alway keep log as final instance field.

private Set<FileEventOptions> optionList;

@Inject
public FileMonitor(final Injector injector,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

injector, watcher and optionList fields can be final, is there a reason why are they not final while some other non-static fields final?

/** The logging system. */
private final Logger log = LoggerFactory.getLogger(getClass());

private List<CheckedFunction2<Config, Binder, FileEventOptions>> bindings = new ArrayList<>();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In FileMonitor keys and options fields are both final and this field is very similar to them, do you have a rule when final should be used for fields?


@Override
public boolean matches(final Path path) {
for (PathMatcher matcher : matchers) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I saw you like to use Java 8 stream API, whouldn't it be better to write as matchers.stream().filter(...).findFirst()... ?

Copy link
Member

@krisztiankocsis krisztiankocsis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pls. check my comments, some of them apply to other places too but I don't want to put redundant reactions

@jknack
Copy link
Member Author

jknack commented Feb 12, 2017

@krisztiankocsis your comments are all in.. I moved to IDEA so there are some unrelated and required pom changes to make IDEA happy.

Thanks

@jknack jknack merged commit f93b732 into master Feb 12, 2017
@jknack jknack mentioned this pull request Feb 12, 2017
@jknack jknack modified the milestones: 1.0.3, 1.1.0 Feb 14, 2017
@jknack jknack deleted the filewatcher branch June 4, 2017 17:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants