diamond源码阅读-目录监控

PathNode(Path)
StandardWatchEventKind(WatchEvent)
Watchable(WatchKey WatchService WatchEvent)
WatchKey(PathNode WatchEvent WatchService)
WatchService(WatchKey Path)
WatchEvent
FileSystem(WatchService)
Path>Watchable

WatchEvent
StandardWatchEventKind(ENTRY_CREATE; ENTRY_DELETE; ENTRY_MODIFY; OVERFLOW)

WatchService  WatchKey

WatchService
1 register 注册监视目录 ,生成watchedKey(遍历子目录,每个文件都注册一个事件),添加到watchedKeys

  1.1

    public WatchKey register(Path root, boolean fireCreatedEventOnIndex, WatchEvent.Kind<?>... events) {
        if (events == null || events.length == 0)
            throw new UnsupportedOperationException("null events");
        if (this.service.isShutdown())
            throw new IllegalStateException("服务已经关闭");
        if (!root.exists())
            throw new IllegalArgumentException("监视的目录不存在");
        WatchKey key = new WatchKey(root, this, fireCreatedEventOnIndex, events);
        resetKey(key);
        return key;
    }

 1.1.1 WatchKey

   public WatchKey(final Path path, final WatchService watcher, boolean fireCreatedEventOnIndex,
            WatchEvent.Kind<?>... events) {
        valid = true;
        this.watcher = watcher;
        // 建立内存索引
        this.root = new PathNode(path, true);
        if (events != null) {
            for (WatchEvent.Kind<?> event : events) {
                filterSet.add(event);
            }
        }
        LinkedList<WatchEvent<?>> changedEvents = new LinkedList<WatchEvent<?>>();
        index(this.root, fireCreatedEventOnIndex, changedEvents);
        this.changedEvents = changedEvents;
    }

  1.1.1.1 WatchKey  为每个目录下子文件建立一个ENTRY_CREATE 监听事件

  private void index(PathNode node, boolean fireCreatedEventOnIndex, LinkedList<WatchEvent<?>> changedEvents) {
        File file = node.getPath().getFile();
        if (!file.isDirectory()) {
            return;
        }
        File[] subFiles = file.listFiles();
        if (subFiles != null) {
            for (File subFile : subFiles) {
                PathNode subNode = new PathNode(new Path(subFile), false);
                if (fireCreatedEventOnIndex) {
                    changedEvents.add(new WatchEvent<Path>(StandardWatchEventKind.ENTRY_CREATE, 1, subNode.getPath()));
                }
                node.addChild(subNode);
                if (subNode.getPath().isDirectory()) {
                    index(subNode, fireCreatedEventOnIndex, changedEvents);
                }
            }
        }
    }

2  启动的一个线程,定时扫描watchedKeys,调用watchedKey.check(),
  如果有变化,watchedKeys 删掉watchedKey ,changedKeys add watchedKey

  

    public WatchService(long checkInterval) {
        service = Executors.newSingleThreadScheduledExecutor();
        service.scheduleAtFixedRate(new CheckThread(), checkInterval, checkInterval, TimeUnit.MILLISECONDS);
    }
    private final class CheckThread implements Runnable {
        public void run() {
            check();
        }
    }

  /**
     * 主动check
     */
    public void check() {
        synchronized (this) {
            Iterator<WatchKey> it = watchedKeys.iterator();
            while (it.hasNext()) {
                WatchKey key = it.next();
                try {
                    if (key.check()) {
                        changedKeys.add(key);
                        it.remove();
                    }
                }
                catch (Throwable t) {
                    log.error("检测WatchKey异常,key=" + key, t);
                }
            }
        }
    }

 2.1 key.check()

  

    boolean check() {
        if (this.changedEvents != null && this.changedEvents.size() > 0)
            return true;
        if (!this.valid)
            return false;
        List<WatchEvent<?>> list = new LinkedList<WatchEvent<?>>();
        if (check(root, list)) {
            this.changedEvents = list;
            return true;
        }
        else {
            return false;
        }
    }

2.1.1

  

    private boolean check(PathNode node, List<WatchEvent<?>> changedEvents) {
        Path nodePath = node.getPath();
        File nodeNewFile = new File(nodePath.getAbsolutePath());
        if (nodePath != null) {
            if (node.isRoot()) {
                if (!nodeNewFile.exists())
                    return fireOnRootDeleted(changedEvents, nodeNewFile);//触发删除事件,添加到当前watchedKey的changedEvents
                else {
                    return checkNodeChildren(node, changedEvents, nodeNewFile);
                }
            }
            else {
                return checkNodeChildren(node, changedEvents, nodeNewFile);
            }
        }
        else
            throw new IllegalStateException("PathNode没有path");
    }

2.1.1.1 监听新增事件 修改事件,若有讲事件添加到当前watchedKey的changedEvents

  private boolean checkNodeChildren(PathNode node, List<WatchEvent<?>> changedEvents, File nodeNewFile) {
        boolean changed = false;
        Iterator<PathNode> it = node.getChildren().iterator();
        // 用于判断是否有新增文件或者目录的现有名称集合
        Set<String> childNameSet = new HashSet<String>();
        while (it.hasNext()) {
            PathNode child = it.next();
            Path childPath = child.getPath();
            childNameSet.add(childPath.getName());
            File childNewFile = new File(childPath.getAbsolutePath());
            // 1、判断文件是否还存在
            if (!childNewFile.exists() && filterSet.contains(StandardWatchEventKind.ENTRY_DELETE)) {
                changed = true;
                changedEvents.add(new WatchEvent<Path>(StandardWatchEventKind.ENTRY_DELETE, 1, childPath));
                it.remove();// 移除节点
            }
            // 2、如果是文件,判断是否被修改
            if (childPath.isFile()) {
                if (checkFile(changedEvents, child, childNewFile) && !changed) {
                    changed = true;
                }

            }
            // 3、递归检测目录
            if (childPath.isDirectory()) {
                if (check(child, changedEvents) && !changed) {
                    changed = true;
                }
            }
        }

        // 查看是否有新增文件
        File[] newChildFiles = nodeNewFile.listFiles();
        if(newChildFiles!=null)
        for (File newChildFile : newChildFiles) {
            if (!childNameSet.contains(newChildFile.getName())
                    && filterSet.contains(StandardWatchEventKind.ENTRY_CREATE)) {
                changed = true;
                Path newChildPath = new Path(newChildFile);
                changedEvents.add(new WatchEvent<Path>(StandardWatchEventKind.ENTRY_CREATE, 1, newChildPath));
                PathNode newSubNode = new PathNode(newChildPath, false);
                node.addChild(newSubNode);// 新增子节点
                // 如果是目录,递归调用
                if (newChildFile.isDirectory()) {
                    checkNodeChildren(newSubNode, changedEvents, newChildFile);
                }
            }
        }
        return changed;
    }

 

3 如何使用

 private void startCheckLocalDir(final String filePath) {
        final WatchService watcher = FileSystem.getDefault().newWatchService();

        Path path = new Path(new File(filePath));
        // 注册事件
        watcher.register(path, true, StandardWatchEventKind.ENTRY_CREATE, StandardWatchEventKind.ENTRY_DELETE,
            StandardWatchEventKind.ENTRY_MODIFY);
        // 第一次运行,主动check
        checkAtFirst(watcher);
        singleExecutor.execute(new Runnable() {
            public void run() {
                log.debug(">>>>>>已经开始监控目录<<<<<<");
                // 无限循环等待事件
                while (isRun) {
                    // 凭证
                    WatchKey key;
                    try {
                        key = watcher.take();
                    }
                    catch (InterruptedException x) {
                        continue;
                    }
                    // reset,如果无效,跳出循环,无效可能是监听的目录被删除
                    if (!processEvents(key)) {
                        log.error("reset unvalid,监控服务失效");
                        break;
                    }
                }
                log.debug(">>>>>>退出监控目录<<<<<<");
                watcher.close();

            }

        });
    }

3.1 处理触发的事件,并继续监听

 /**
     * 处理触发的事件
     * 
     * @param key
     * @return
     */
    @SuppressWarnings({ "unchecked" })
    private boolean processEvents(WatchKey key) {
        /**
         * 获取事件集合
         */
        for (WatchEvent<?> event : key.pollEvents()) {
            // 事件的类型
            // WatchEvent.Kind<?> kind = event.kind();

            // 通过context方法得到发生事件的path
            WatchEvent<Path> ev = (WatchEvent<Path>) event;
            Path eventPath = ev.context();

            String realPath = eventPath.getAbsolutePath();
            if (ev.kind() == StandardWatchEventKind.ENTRY_CREATE || ev.kind() == StandardWatchEventKind.ENTRY_MODIFY) {

                String grandpaDir = null;
                try {
                    grandpaDir = FileUtils.getGrandpaDir(realPath);
                }
                catch (Exception e1) {

                }
                if (!Constants.BASE_DIR.equals(grandpaDir)) {
                    log.error("无效的文件进入监控目录: " + realPath);
                    continue;
                }
                existFiles.put(realPath, System.currentTimeMillis());
                if (log.isInfoEnabled()) {
                    log.info(realPath + "文件被添加或更新");
                }
            }
            else if (ev.kind() == StandardWatchEventKind.ENTRY_DELETE) {
                String grandpaDir = null;
                try {
                    grandpaDir = FileUtils.getGrandpaDir(realPath);
                }
                catch (Exception e1) {

                }
                if (Constants.BASE_DIR.equals(grandpaDir)) {
                    // 删除的是文件
                    existFiles.remove(realPath);
                    if (log.isInfoEnabled()) {
                        log.info(realPath + "文件被被删除");
                    }
                }
                else {
                    // 删除的是目录
                    Set<String> keySet = new HashSet<String>(existFiles.keySet());
                    for (String filePath : keySet) {
                        if (filePath.startsWith(realPath)) {
                            existFiles.remove(filePath);
                            if (log.isInfoEnabled()) {
                                log.info(filePath + "文件被删除");
                            }
                        }
                    }

                }
            }
        }
        return key.reset();//WatchService 继续监听
    }

 

posted @ 2016-10-25 16:32  穿林度水  阅读(534)  评论(0编辑  收藏  举报