Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WatchService with spring

I am trying to execute "WatchService" with spring but it is looking impossible because when I am trying to run this service at the time of application context but loading of spring context becoming stop when control come at

key = watcher.take();

due to this loading of application context is not happening.

below is the full code

@Component
public class DirectoryWatchDemo {


    @PostConstruct
    public static void test(){
        try {
            WatchService watcher = FileSystems.getDefault().newWatchService();
            Path dir = Paths.get("C:/test");
            dir.register(watcher, ENTRY_CREATE);

            System.out.println("Watch Service registered for dir: " + dir.getFileName());

            while (true) {
                WatchKey key;
                try {
                    key = watcher.take();
                } catch (InterruptedException ex) {
                    return;
                }

                for (WatchEvent<?> event : key.pollEvents()) {
                    WatchEvent.Kind<?> kind = event.kind();

                    @SuppressWarnings("unchecked")
                    WatchEvent<Path> ev = (WatchEvent<Path>) event;
                    Path fileName = ev.context();

                    System.out.println(kind.name() + ": " + fileName);

                    if (kind == ENTRY_MODIFY &&
                            fileName.toString().equals("DirectoryWatchDemo.java")) {
                        System.out.println("My source file has changed!!!");
                    }
                }

                boolean valid = key.reset();
                if (!valid) {
                    break;
                }
            }

        } catch (IOException ex) {
            System.err.println(ex);
        }
    }

}

I am doing in this way because I do not want to execute "WatchService" manually.

like image 923
Rajeev Avatar asked Nov 02 '25 18:11

Rajeev


2 Answers

It is because you have an endless loop, and therefore the method invocation of @PostConstruct never return.

(it seams strange to me, that @PostConstruct works with static methods, but maybe this works)

So the solution is that you start an new thread for your watcher. You could do this in different ways:

  • just start an new thread
  • Add @Async to that method (I do not know if this works for @PostConstruct methods) (the major drawback is, that this starts BEFOR the complete application context is initialize)
  • abuse the @Scheduler annotation: @Scheduled(fixedDelay = Long.MAX_VALUE) - (the advantage over @PostConstruct+@Async is: that it works for sure and it starts just AFTER the complete context is initialized)
like image 139
Ralph Avatar answered Nov 05 '25 09:11

Ralph


WatchService.take waits for the next watch key: "Retrieves and removes next watch key, waiting if none are yet present."

PostConstruct - which is a Java annotation, versus a Spring annotation - "is used on a method that needs to be executed after dependency injection is done to perform any initialization. This method MUST be invoked before the class is put into service." Based on this documentation it seems PostConstruct must return before the bean can be put into service ("MUST be invoked before the class is put into service").

But your PostConstruct method does not return; so PostConstruct is not what you need.

You might consider implementing the Spring InitializingBean interface, which provides a callback method afterPropertiesSet. It should allow you to start this type of service method.

Otherwise you could look at the Apache VFS2 virtual file system, which includes a folder watcher. That is what my project uses; it was pretty easy to start the watcher at system start time; and also, it watches for file delete, update, and create events (unlike the Camel file watcher).

like image 23
David Miller Avatar answered Nov 05 '25 09:11

David Miller