thmasker
thmasker

Reputation: 648

Restart WatchService after exceptions

I'm using Java's WatchService API within my Spring Boot application to monitor a directory, and perform some actions on created files. This process is executed asynchronously: it starts automatically right after the application is ready and monitors the directory in the background until the application is stopped.

This is the configuration class:

@Configuration
public class DirectoryWatcherConfig {

    @Value("${path}")
    private String path;

    @Bean
    public WatchService watchService() throws IOException {
        WatchService watchService = FileSystems.getDefault().newWatchService();
        Path directoryPath = Paths.get(path);
        directoryPath.register(watchService, StandardWatchEventKinds.ENTRY_CREATE);
        return watchService;
    }

}

And this is the monitoring service:

@Service
@RequiredArgsConstructor
public class DirectoryWatcherService {

    private final WatchService watchService;

    @Async
    @EventListener(ApplicationReadyEvent.class)
    public void startWatching() throws InterruptedException {
        WatchKey key;
        while ((key = watchService.take()) != null) {
            for (WatchEvent<?> event : key.pollEvents()) {
                // actions on created files
            }

            key.reset();
        }
    }

}

This code is working as expected, with the following exception, which I'd like to fix:

Upvotes: 4

Views: 420

Answers (2)

thmasker
thmasker

Reputation: 648

Actually the solution was quite simple. Wrapping the desired actions with try/catch (catching desired exceptions) in DirectoryWatcherService like this allows the thread to continue monitoring directories:

@Service
@RequiredArgsConstructor
public class DirectoryWatcherService {

    private final WatchService watchService;

    @Async
    @EventListener(ApplicationReadyEvent.class)
    public void startWatching() throws InterruptedException {
        WatchKey key;
        while ((key = watchService.take()) != null) {
            for (WatchEvent<?> event : key.pollEvents()) {
                try {
                    // actions on created files
                } catch (RuntimeException ex) {
                    // log exception or whatever you choose, as long as execution continues
                }
            }

            key.reset();
        }
    }

}

Upvotes: 0

Jonxag
Jonxag

Reputation: 787

You should change the loop with and add a try/Catch, in the catch restarting the service. As you commented you need to keep in even if is interrupted , so you will need the use of ExecutorService. Declare the Executor out of the method

@Autowired
private ExecutorService executor;

and inside the method something similar to my last answer but using the executor

Runnable task = () -> {
        while (true) {
            try {
                WatchKey key = watchService.take();
                if (key != null) {
                    for (WatchEvent<?> event : key.pollEvents()) {
                        // Perform actions on created files here
                    }
                    key.reset();
                }
            } catch (Exception e) {
                // Wait for some time before starting the thread again
                Thread.sleep(5000);
            }
        }
    };
    //submit the task to the executor
    executor.submit(task);

Upvotes: 2

Related Questions