• 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 继续监听
        }
  • 相关阅读:
    深入解析Hibernate核心接口
    Hibernate基本原理
    深入hibernate的三种状态
    Hibernate commit() 和flush() 的区别
    Hibernate中的merge使用详情解说
    Record is locked by another user
    Vue路由router-link的使用
    Vue-router的基本使用
    Vue把父组件的方法传递给子组件调用(评论列表例子)
    Vue中子组件调用父组件的方法
  • 原文地址:https://www.cnblogs.com/clds/p/5997195.html
Copyright © 2020-2023  润新知