Package ca.pgon.st.light7.filesystemupdatewatcher

Source Code of ca.pgon.st.light7.filesystemupdatewatcher.FileSystemUpdateWatcher

/*
    Small Tools - Some basic libraries for developing and testing http://www.pgon.ca/st
    Copyright (C) 2013-2014 Small Tools (info@pgon.ca)

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
*/
package ca.pgon.st.light7.filesystemupdatewatcher;

import java.io.File;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import ca.pgon.st.light6.Assert;
import ca.pgon.st.light6.exception.StLightException;

/**
* This class is looking at any changes made to any file in the directory and their sub-directory if needed.
*
* On initialization, it creates a separate thread that will call the observers when new events are ready.
*
* Default:
* <ul>
* <li>recursive = false</li>
* </ul>
*
* @author Simon Levesque
*
*/
public class FileSystemUpdateWatcher extends Thread {

    // Options
    private Path basePath;
    private boolean recursive = false;

    // Internals
    private boolean initialized = false;
    private WatchService fsWatchService;
    private List<FileSystemUpdateHandler> fileSystemUpdateHandlers = new ArrayList<>();

    private Map<WatchKey, Path> pathByKey = new HashMap<WatchKey, Path>();

    public FileSystemUpdateWatcher(File basePath) {
        this.basePath = basePath.toPath();
    }

    public FileSystemUpdateWatcher(Path basePath) {
        this.basePath = basePath;
    }

    public FileSystemUpdateWatcher(String basePath) {
        this.basePath = Paths.get(basePath);
    }

    /**
     * Add an handler.
     *
     * @return this
     */
    public FileSystemUpdateWatcher addHandler(FileSystemUpdateHandler fileSystemUpdateHandler) {
        fileSystemUpdateHandlers.add(fileSystemUpdateHandler);
        return this;
    }

    /**
     * Call after setting this object to make it work.
     *
     * @return this
     */
    public FileSystemUpdateWatcher init() {

        Assert.assertFalse(initialized, "Already initialized");
        Assert.assertFalse(fileSystemUpdateHandlers.isEmpty(), "There are no handlers");

        // Register the directories
        try {
            fsWatchService = FileSystems.getDefault().newWatchService();
            registerRecursively(basePath);
        } catch (IOException e) {
            throw new StLightException(e);
        }

        // Start the watch thread
        start();

        initialized = true;

        return this;
    }

    /**
     * Tells if the watch is recursive.
     *
     * @return recursive
     */
    public boolean isRecursive() {
        return recursive;
    }

    /**
     * Register a new path to check.
     *
     * @param path
     *            the path
     */
    protected void register(Path path) {
        try {
            WatchKey key = path.register(fsWatchService, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE, StandardWatchEventKinds.ENTRY_MODIFY);
            pathByKey.put(key, path);
        } catch (IOException e) {
            throw new StLightException(e);
        }
    }

    /**
     * Register a new path to check and all its children if it is set recursive.
     *
     * @param path
     *            the path
     */
    private void registerRecursively(Path path) {

        register(path);

        File file = path.toFile();
        if (recursive && file.isDirectory()) {
            // Go through sub-folders
            for (File sub : file.listFiles()) {
                if (sub.isDirectory()) {
                    registerRecursively(sub.toPath());
                }
            }

        }
    }

    @SuppressWarnings("unchecked")
    @Override
    public void run() {
        for (;;) {

            // Wait for the next event
            WatchKey key;
            try {
                key = fsWatchService.take();
            } catch (InterruptedException e) {
                throw new StLightException(e);
            }

            // Go through all the events
            for (WatchEvent<?> event : key.pollEvents()) {
                WatchEvent<Path> pathEvent = (WatchEvent<Path>) event;

                // Check the path
                Path completePath = pathByKey.get(key).resolve(pathEvent.context());
                File completeFile = completePath.toFile();

                // Check the event
                WatchEvent.Kind<Path> kind = pathEvent.kind();
                if (StandardWatchEventKinds.OVERFLOW.equals(kind)) {
                    continue;
                } else if (StandardWatchEventKinds.ENTRY_CREATE.equals(kind)) {
                    // Check if is a directory and we want to register it
                    if (recursive && Files.isDirectory(completePath, LinkOption.NOFOLLOW_LINKS)) {
                        registerRecursively(completePath);
                    }

                    for (FileSystemUpdateHandler handler : fileSystemUpdateHandlers) {
                        handler.created(completeFile);
                    }
                } else if (StandardWatchEventKinds.ENTRY_MODIFY.equals(kind)) {
                    for (FileSystemUpdateHandler handler : fileSystemUpdateHandlers) {
                        handler.modified(completeFile);
                    }
                } else if (StandardWatchEventKinds.ENTRY_DELETE.equals(kind)) {
                    for (FileSystemUpdateHandler handler : fileSystemUpdateHandlers) {
                        handler.deleted(completeFile);
                    }
                }

            }

            // Reset
            if (!key.reset()) {
                pathByKey.remove(key);
            }

        }
    }

    /**
     * Change the recursive parameter.
     *
     * @param recursive
     * @return this
     */
    public FileSystemUpdateWatcher setRecursive(boolean recursive) {

        Assert.assertFalse(initialized, "Cannot set recursive when already initialized");

        this.recursive = recursive;

        return this;
    }

}
TOP

Related Classes of ca.pgon.st.light7.filesystemupdatewatcher.FileSystemUpdateWatcher

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.