Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to monitor folder/directory in spring?

I wan't to write Spring Boot Application in spring which will be monitoring directory in windows, and when I change sub folder or add new one or delete existing one I wanna get information about that.

How can i do that? I have read this one: http://docs.spring.io/spring-integration/reference/html/files.html and each result under 'spring file watcher' in google, but I can't find solution...

Do you have a good article or example with something like this? I wan't it to like like this:

@SpringBootApplication
@EnableIntegration
public class SpringApp{

    public static void main(String[] args) {
        SpringApplication.run(SpringApp.class, args);
    }

    @Bean
    public WatchService watcherService() {
        ...//define WatchService here
    }
}

Regards

like image 420
amkz Avatar asked Oct 13 '16 12:10

amkz


2 Answers

spring-boot-devtools has FileSystemWatcher

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
</dependency>

FileWatcherConfig

@Configuration
public class FileWatcherConfig {
    @Bean
    public FileSystemWatcher fileSystemWatcher() {
        FileSystemWatcher fileSystemWatcher = new FileSystemWatcher(true, Duration.ofMillis(5000L), Duration.ofMillis(3000L));
        fileSystemWatcher.addSourceFolder(new File("/path/to/folder"));
        fileSystemWatcher.addListener(new MyFileChangeListener());
        fileSystemWatcher.start();
        System.out.println("started fileSystemWatcher");
        return fileSystemWatcher;
    }

    @PreDestroy
    public void onDestroy() throws Exception {
        fileSystemWatcher().stop();
    }
}

MyFileChangeListener

@Component
public class MyFileChangeListener implements FileChangeListener {

    @Override
    public void onChange(Set<ChangedFiles> changeSet) {
        for(ChangedFiles cfiles : changeSet) {
            for(ChangedFile cfile: cfiles.getFiles()) {
                if( /* (cfile.getType().equals(Type.MODIFY) 
                     || cfile.getType().equals(Type.ADD)  
                     || cfile.getType().equals(Type.DELETE) ) && */ !isLocked(cfile.getFile().toPath())) {
                    System.out.println("Operation: " + cfile.getType() 
                      + " On file: "+ cfile.getFile().getName() + " is done");
                }
            }
        }
    }
    
    private boolean isLocked(Path path) {
        try (FileChannel ch = FileChannel.open(path, StandardOpenOption.WRITE); FileLock lock = ch.tryLock()) {
            return lock == null;
        } catch (IOException e) {
            return true;
        }
    }

}
like image 108
Sully Avatar answered Sep 25 '22 05:09

Sully


From Java 7 there is WatchService - it will be the best solution.

Spring configuration could be like the following:

@Slf4j
@Configuration
public class MonitoringConfig {

    @Value("${monitoring-folder}")
    private String folderPath;

    @Bean
    public WatchService watchService() {
        log.debug("MONITORING_FOLDER: {}", folderPath);
        WatchService watchService = null;
        try {
            watchService = FileSystems.getDefault().newWatchService();
            Path path = Paths.get(folderPath);

            if (!Files.isDirectory(path)) {
                throw new RuntimeException("incorrect monitoring folder: " + path);
            }

            path.register(
                    watchService,
                    StandardWatchEventKinds.ENTRY_DELETE,
                    StandardWatchEventKinds.ENTRY_MODIFY,
                    StandardWatchEventKinds.ENTRY_CREATE
            );
        } catch (IOException e) {
            log.error("exception for watch service creation:", e);
        }
        return watchService;
    }
}

And Bean for launching monitoring itself:

@Slf4j
@Service
@AllArgsConstructor
public class MonitoringServiceImpl {

    private final WatchService watchService;

    @Async
    @PostConstruct
    public void launchMonitoring() {
        log.info("START_MONITORING");
        try {
            WatchKey key;
            while ((key = watchService.take()) != null) {
                for (WatchEvent<?> event : key.pollEvents()) {
                    log.debug("Event kind: {}; File affected: {}", event.kind(), event.context());
                }
                key.reset();
            }
        } catch (InterruptedException e) {
            log.warn("interrupted exception for monitoring service");
        }
    }

    @PreDestroy
    public void stopMonitoring() {
        log.info("STOP_MONITORING");

        if (watchService != null) {
            try {
                watchService.close();
            } catch (IOException e) {
                log.error("exception while closing the monitoring service");
            }
        }
    }
}

Also, you have to set @EnableAsync for your application class (it configuration).

and snipped from application.yml:

monitoring-folder: C:\Users\nazar_art

Tested with Spring Boot 2.3.1.


Also used configuration for Async pool:

@Slf4j
@EnableAsync
@Configuration
@AllArgsConstructor
@EnableConfigurationProperties(AsyncProperties.class)
public class AsyncConfiguration implements AsyncConfigurer {

    private final AsyncProperties properties;

    @Override
    @Bean(name = "taskExecutor")
    public Executor getAsyncExecutor() {
        log.debug("Creating Async Task Executor");
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setCorePoolSize(properties.getCorePoolSize());
        taskExecutor.setMaxPoolSize(properties.getMaxPoolSize());
        taskExecutor.setQueueCapacity(properties.getQueueCapacity());
        taskExecutor.setThreadNamePrefix(properties.getThreadName());
        taskExecutor.initialize();
        return taskExecutor;
    }

    @Bean
    public TaskScheduler taskScheduler() {
        return new ConcurrentTaskScheduler();
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new CustomAsyncExceptionHandler();
    }
}

Where the custom async exception handler is:

@Slf4j
public class CustomAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {

    @Override
    public void handleUncaughtException(Throwable throwable, Method method, Object... objects) {
        log.error("Exception for Async execution: ", throwable);
        log.error("Method name - {}", method.getName());
        for (Object param : objects) {
            log.error("Parameter value - {}", param);
        }
    }
}

Configuration at properties file:

async-monitoring:
  core-pool-size: 10
  max-pool-size: 20
  queue-capacity: 1024
  thread-name: 'async-ex-'
like image 41
catch23 Avatar answered Sep 25 '22 05:09

catch23