• 通过Java SE 7自带的监控服务(WatchService API)实现类似.NET FileWatcher的功能


    Java SE 7 Tutorial中增加了一个监控目录变更情况的示例,用于介绍其新发布的WatchService API。

    但对于用惯了.NET FileWatcher的用户而言,如果用于项目我认为它有两个欠缺:

    1、应该提供一个独立线程后台运行机制,让这个监控过程自己在后台转,不影响前端处理

    2、 Java不像.NET有内置的源生事件机制,不过可以借助它内置的Observer/Observable对象用观察者模式实现准事件

    下面是把Java SE Tutorial示例中无关内容删除,补充上述两个扩展后的实现,因为这个API比较新,也希望能和大家多多探讨:

    1、参考.NET定义事件参数对象

    复制代码
    package marvellousworks.practicalpattern.concept.unittest;

    import java.nio.file.WatchEvent.Kind;

    /**
     * 文件系统事件类型
     * @author wangxiang
     *
     */
    public final class FileSystemEventArgs {
        private final String fileName;
        private final Kind<?> kind;
        
        public FileSystemEventArgs(String fileName, Kind<?> kind){
            this.fileName = fileName;
            this.kind = kind;
        }
        
        /**
         * 文件的路径
         */
        public String getFileName(){return fileName;}
        
        /**
         * 操作类型:变更、创建、删除
         */
        @SuppressWarnings("rawtypes")
        public Kind getKind(){return kind;}
    }
    复制代码

     2、定义DirectoryWatcher,用于监控某个文件夹,至于如何扩展FileWatcher则可以在这个基础上通过限定文件名称和操作类型的方式扩展

    复制代码
    package marvellousworks.practicalpattern.concept.unittest;

    import java.io.IOException;
    import java.nio.file.FileSystems;
    import java.nio.file.Path;
    import java.nio.file.Paths;
    import java.nio.file.WatchEvent;
    import java.nio.file.WatchEvent.Kind;
    import java.nio.file.WatchKey;
    import java.nio.file.WatchService;
    import java.util.Observable;
    import java.util.concurrent.Callable;
    import java.util.concurrent.Executor;
    import java.util.concurrent.Executors;
    import java.util.concurrent.FutureTask;

    import static java.nio.file.StandardWatchEventKinds.*;

    /**
     * 监控一个目录内文件的更新、创建和删除事件(不包括子目录)
     * 
     * 对于http://download.oracle.com/javase/tutorial/essential/io/notification.html进行了改造
     * 使其更接近.NET的DirectoryWatcher使用习惯
     * 
     * 由于java没有类似.NET源生的事件机制
     * 因此实现上采用了Java SE自带的Observer/Observable对象对外抛出“假”事件
     * 
     * 适于Java SE 7
     * 
     * @author wangxiang
     *
     */
    public class DirectoryWatcher extends Observable{

        private WatchService watcher;
        private Path path;
        private WatchKey key;
        private Executor executor = Executors.newSingleThreadExecutor();
        
        FutureTask<Integer> task = new FutureTask<Integer>(
                new Callable<Integer>(){
                    public Integer call() throws InterruptedException{
                        processEvents();
                        return Integer.valueOf(0);}});

        @SuppressWarnings("unchecked")
        static <T> WatchEvent<T> cast(WatchEvent<?> event) {
            return (WatchEvent<T>) event;
        }

        public DirectoryWatcher(String dir) throws IOException {
            watcher = FileSystems.getDefault().newWatchService();
            path = Paths.get(dir);
            //    监控目录内文件的更新、创建和删除事件
            key = path.register(watcher, ENTRY_MODIFY, ENTRY_CREATE, ENTRY_DELETE);
        }

        /**
         * 启动监控过程
         */
        public void execute(){
            // 通过线程池启动一个额外的线程加载Watching过程
            executor.execute(task);        
        }
        
        /**
         * 关闭后的对象无法重新启动
         * @throws IOException
         */
        public void shutdown() throws IOException {
            watcher.close();
            executor = null;
        }

        /**
         * 监控文件系统事件
         */
        void processEvents() {
            while (true) {
                // 等待直到获得事件信号
                WatchKey signal;
                try {
                    signal = watcher.take();
                } catch (InterruptedException x) {
                    return;
                }

                for (WatchEvent<?> event : signal.pollEvents()) {
                    Kind<?> kind = event.kind();

                    // TBD - provide example of how OVERFLOW event is handled
                    if (kind == OVERFLOW) {
                        continue;
                    }

                    // Context for directory entry event is the file name of entry
                    WatchEvent<Path> ev = cast(event);
                    Path name = ev.context();                notifiy(name.getFileName().toString(), kind);            }            //    为监控下一个通知做准备            key.reset();        }    }        /**     * 通知外部各个Observer目录有新的事件更新     */    void notifiy(String fileName, Kind<?> kind){        // 标注目录已经被做了更改        setChanged();        //     主动通知各个观察者目标对象状态的变更        //    这里采用的是观察者模式的“推”方式        notifyObservers(new FileSystemEventArgs(fileName, kind));    }}
    复制代码

     3、单元测试

    复制代码
    package marvellousworks.practicalpattern.concept.unittest;

    import static org.junit.Assert.*;

    import java.io.File;
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Observable;
    import java.util.Observer;

    import org.junit.Test;
    import static java.nio.file.StandardWatchEventKinds.*;

    public class DirectoryWatcherFixture {

        private static final String DIR_PATH =System.getProperty("user.dir"); 
        private static final File DIR = new File(DIR_PATH);
        private static final String SUFFIX = ".txt";
        private static final String PREFIX = "test";
        private static final int ADD_TIMES = 3;

        /**
         * 观察者
         * @author wangxiang
         *
         */
        public class Logger implements Observer{
            @Override
            public void update(Observable observable, Object eventArgs) {
                FileSystemEventArgs args = (FileSystemEventArgs) eventArgs;
                System.out.printf("%s has been %s ", args.getFileName(), args.getKind());
                assertTrue(args.getFileName().startsWith(PREFIX));
                    assertEquals(ENTRY_CREATE, args.getKind());
            }
        }
        
        @Test
        public void testWatchFile() throws IOException, InterruptedException{
            DirectoryWatcher watcher = new DirectoryWatcher(DIR_PATH);
            Logger l1 = new Logger();
            watcher.addObserver(l1);
            watcher.execute();
            
            //    创建一系列临时文件
            List<String> files = new ArrayList<>();
            for(int i=0; i<ADD_TIMES; i++){
                files.add(File.createTempFile(PREFIX, SUFFIX, DIR).toString());
            }
            
            //    延迟等待后台任务的执行
            Thread.sleep(4000);
            watcher.shutdown();
            System.out.println("finished");
        }
    }
    复制代码

     Console窗口显示的测试内容

    test5769907807190550725.txt has been ENTRY_CREATE
    test4657672246246330348.txt has been ENTRY_CREATE
    test1823102943601166149.txt has been ENTRY_CREATE
    finished
  • 相关阅读:
    转载 | python inferface使用
    gasshopper之python电池输出dict结构
    Tensorflow 2.0 搭建神经网络(局部)
    grasshopper之python电池执行逻辑
    springboot中给属性文件内容加密(application.properties,application.yml)
    使用docker安装rabbitmq
    关于宝塔Linux面板
    winsw来设置window自启服务
    docker的使用,Nginx,Tomcat,Portainer可视化
    内网穿透(windows)
  • 原文地址:https://www.cnblogs.com/duanxz/p/4036786.html
Copyright © 2020-2023  润新知