4

I have this Java code which I want to use to monitor file changes into directory:

private static String folderPath = "D:\\Import";

public static void main(final String[] args) throws IOException, InterruptedException {

    System.out.println("Running file verifier");
    System.out.println("monitoring folder " + folderPath);
    EntityImportRequestsJob sql = new EntityImportRequestsJob();

    WatchService watchService = FileSystems.getDefault().newWatchService();
    Path path = Paths.get(folderPath);
    path.register(watchService, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE, StandardWatchEventKinds.ENTRY_MODIFY);
    WatchKey key;

    while ((key = watchService.take()) != null) {
        for (WatchEvent<?> event : key.pollEvents()) {
            System.out.println("Event kind:" + event.kind() + ". File affected: " + event.context() + ".");
            
            if (event.kind().equals(StandardWatchEventKinds.ENTRY_DELETE)) {
                Instant start = Instant.now();

                boolean flag = true;

                while(flag) {
                    while ((key = watchService.take()) != null) {
                        HashMap<String, List> map = sql.checkFileImport();

                        List values = map.get(event.context()); // get values by file name
                        if(values.contains("Completed")){
                            // exit the monitoring while loop
                            flag = false;
                        }
                    }
                    Thread.sleep(1000);
                }

                Instant end = Instant.now();
                System.out.println(Duration.between(start,end));

                long seconds = TimeUnit.MILLISECONDS.toSeconds(Duration.between(start,end).getSeconds());
                long minutes = TimeUnit.MILLISECONDS.toMinutes(Duration.between(start,end).getSeconds());

                System.out.format("Execution time %d minutes %d seconds", minutes, seconds);
            }

        }

        key.reset();
    }

    watchService.close();
}

How I can start this code when Spring Project is successfully started?

António Ribeiro
  • 4,129
  • 5
  • 32
  • 49
Peter Penzov
  • 1,126
  • 134
  • 430
  • 808
  • 2
    It's not exactly clear what you want: do you want a Spring Boot application that just does that? Do you want that to run in the background of an application that does other things? – ekalin Jan 22 '23 at 14:11

4 Answers4

1

You can use @PostConstruct annotation on your method. This will execute the method right after the bean is created and file will be checked.

Create a class with @Component annotation which has a method with @PostConstruct annotation that contains your code. I am not sure if your code is constantly checking the file or whether you need to constantly execute(call the method). If your code is already constantly checking the file, then calling the method only once with @PostConstruct will be enough. Otherwise, you can use an infinity loop or scheduler like the 1st answer.


@Component
public class FileChecker {

    @PostConstruct
    public void executeCheck(){
        System.out.println("Running file verifier");
        System.out.println("monitoring folder " + folderPath);
        EntityImportRequestsJob sql = new EntityImportRequestsJob();

        WatchService watchService = FileSystems.getDefault().newWatchService();
        Path path = Paths.get(folderPath);
        path.register(watchService, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE, StandardWatchEventKinds.ENTRY_MODIFY);
        WatchKey key;
        while ((key = watchService.take()) != null) {
            for (WatchEvent<?> event : key.pollEvents()) {
                System.out.println("Event kind:" + event.kind() + ". File affected: " + event.context() + ".");
                if(event.kind().equals(StandardWatchEventKinds.ENTRY_DELETE)){
                    Instant start = Instant.now();

                    boolean flag = true;

                    while(flag) {
                        while ((key = watchService.take()) != null) {
                            HashMap<String, List> map = sql.checkFileImport();

                            List values = map.get(event.context()); // get values by file name
                            if(values.contains("Completed")){
                                // exit the monitoring while loop
                                flag = false;
                            }
                        }
                        Thread.sleep(1000);
                    }

                    Instant end = Instant.now();
                    System.out.println(Duration.between(start,end));

                    long seconds = TimeUnit.MILLISECONDS.toSeconds(Duration.between(start,end).getSeconds());
                    long minutes = TimeUnit.MILLISECONDS.toMinutes(Duration.between(start,end).getSeconds());

                    System.out.format("Execution time %d minutes %d seconds", minutes, seconds);


                }

            }
            key.reset();
        }
        watchService.close();
    }
}
mahfuj asif
  • 1,691
  • 1
  • 11
  • 32
1

You can start this code when a Spring project is successfully started by creating a Bean for it and annotating it with @EventListener and ApplicationReadyEvent like this:

@EventListener(ApplicationReadyEvent.class)
@Component
public class FileMonitor {
    private static String folderPath = "D:\\Import";

public void startMonitoring() throws IOException, InterruptedException {
    System.out.println("Running file verifier");
    System.out.println("monitoring folder " + folderPath);
    EntitiesImportRequestsJob sql = new EntitiesImportRequestsJob();

    WatchService watchService = FileSystems.getDefault().newWatchService();
    Path path = Paths.get(folderPath);
    path.register(watchService, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE, StandardWatchEventKinds.ENTRY_MODIFY);
    WatchKey key;

    while ((key = watchService.take()) != null) {
        for (WatchEvent<?> event : key.pollEvents()) {
            System.out.println("Event kind:" + event.kind() + ". File affected: " + event.context() + ".");

            if (event.kind().equals(StandardWatchEventKinds.ENTRY_DELETE)) {
                Instant start = Instant.now();

                boolean flag = true;

                while(flag) {
                    while ((key = watchService.take()) != null) {
                        HashMap<String, List> map = sql.checkFileImport();

                        List values = map.get(event.context()); // get values by file name
                        if(values.contains("Completed")){
                            // exit the monitoring while loop
                            flag = false;
                        }
                    }
                    Thread.sleep(1000);
                }

                Instant end = Instant.now();
                System.out.println(Duration.between(start,end));

                long seconds = TimeUnit.MILLISECONDS.toSeconds(Duration.between(start,end).getSeconds());
                long minutes = TimeUnit.MILLISECONDS.toMinutes(Duration.between(start,end).getSeconds());

                System.out.format("Execution time %d minutes %d seconds", minutes, seconds);
            }

        }

        key.reset();
    }

    watchService.close();
}

}

Make sure the FileMonitor class is in a package that is scanned by Spring.

And also make sure EntitiesImportRequestsJob is properly instantiated and managed by spring container, otherwise you will get a NullPointerException

Catalin Podariu
  • 148
  • 2
  • 8
0

It seems you want to schedule the task of checking the directory changes. If yes, What you can do is enable scheduling for this task. For non-production apps, directly use @EnableScheduling on the class and create a method with annotation @Scheduled(fixedRate=XXXXXX) where XXXXXX is your delay for the scheduled job to run.

Also, you can add a cron expression inside like @Scheduled(cron = "0 * * * * MON-FRI").

For prod-ready applications, I prefer the external schedulers to help to run a jar on a schedule or create a FAAS (for example, lambda expression).

Neeraj S
  • 19
  • 4
0

Your code looks like a constantly running background job. To run it and not block the main thread with all those watchService.take() and Thread.sleep(1000) the best approach would be to use the Spring's TaskExecutor abstraction. You'll have to wrap your code into a Runnable which then could be fed to a task executor. To launch this task I'd use an ApplicationRunner - Spring will run it after the application context is fully initialized.

Below is an example of what it might look like (uses lombok library).

Runnable:

@Slf4j
class FileVerificationTask implements Runnable {

    private static String folderPath = "D:\\Import";

    @SneakyThrows
    @Override
    public void run() {
        log.info("Running file verifier");

        // put your code here
    }
}

Launcher:

@Service
@Slf4j
public class FileVerifier implements ApplicationRunner {
    private final TaskExecutor taskExecutor;

    public FileVerifier(TaskExecutor taskExecutor) {
        this.taskExecutor = taskExecutor;
    }

    @Override
    public void run(ApplicationArguments args) {
        taskExecutor.execute(new FileVerificationTask());
    }
}
dekkard
  • 6,121
  • 1
  • 16
  • 26