java 1.7watchservice特性学习 文件变更监控

Deadly 提交于 2019-12-17 16:17:39

【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>

一、首先概述1.7版本上面的这个java.nio.file.WatchService类

1.1 WatchService 可以干什么?

WatchService可以帮助我们监控一些文件是否做了更改,如果文件做了更改,可以帮助我们去做一些响应,比如,配置文件修改了,那么系统应该重新加载一下配置文件,这样可以将最新的配置加载进来。

 

1.2 完成文件监控一些很重要的类

The watch service exits when either the thread exits or when it is closed (by invoking its closed method。当前启动的线程退出或者调用closed方法关闭的时候,watch service也会推出。

1.2.1 java.nio.file.WatchKey类 

此类代表着一个Object(文件或者文件夹) 注册到WatchService后,返回一个WatchKey类。反应当前所注册的Object状态的类。此类封装了不同事件的状态值,比如,当文件(文件夹)被修改或者被删除或者创建的时候,此类首先产生一个信号量,等待消费者来取并且该WatchKey将会进入到WatchService的队列中。如果WatchKey已经进入到队列当中,但是又有了变化,将不会再次进入到队列当中。

WatchService.take()方法可以取到队列的WatchKey.

1.2.2 java.nio.file.Path类

此类代表文件系统上面的一个文件

1.2.2 java.nio.file.WatchEvent类

此类代表文件的一个具体事件。

WatchEvent

1.3 原理探讨(参考

To implement this functionality, called file change notification, a program must be able to detect what is happening to the relevant directory on the file system. One way to do so is to poll the file system looking for changes, but this approach is inefficient. It does not scale to applications that have hundreds of open files or directories to monitor.

要做到文件变更提醒功能,有一种办法就是轮询文件系统上面的文件确认是否有变更,但是显然这种做法效率不高。

The java.nio.file package provides a file change notification API, called the Watch Service API. This API enables you to register a directory (or directories) with the watch service. When registering, you tell the service which types of events you are interested in: file creation, file deletion, or file modification. When the service detects an event of interest, it is forwarded to the registered process. The registered process has a thread (or a pool of threads) dedicated to watching for any events it has registered for. When an event comes in, it is handled as needed.

Java WatchService 服务提供API,可以注册你感兴趣的事件,当service 发现注册的事件后,就交给处理线程做处理。

1.4 使用注意事项

1.4.1 收到变更事件时候,是否变更已经完成

当收到一个文件变更事件的时候,并不表示该文件已经变更完毕,有可能还在变更当中。

1.4.2 什么时候使用Watch Service比较好.

The Watch Service API is designed for applications that need to be notified about file change events. It is well suited for any application, like an editor or IDE, that potentially has many open files and needs to ensure that the files are synchronized with the file system. It is also well suited for an application server that watches a directory, perhaps waiting for .jsp or .jar files to drop, in order to deploy them.

This API is not designed for indexing a hard drive. Most file system implementations have native support for file change notification. The Watch Service API takes advantage of this support where available. However, when a file system does not support this mechanism, the Watch Service will poll the file system, waiting for events.

1.5 亲自测试

Oracle 官方给出的例子代码:

package com.hpe.common.audit;


/*
 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 *   - Neither the name of Oracle nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

import java.nio.file.*;
import static java.nio.file.StandardWatchEventKinds.*;
import static java.nio.file.LinkOption.*;
import java.nio.file.attribute.*;
import java.io.*;
import java.util.*;

/**
 * Example to watch a directory (or tree) for changes to files.
 */

public class WatchDir {

    private final WatchService watcher;
    private final Map<WatchKey,Path> keys;
    private final boolean recursive;
    private boolean trace = false;

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

    /**
     * Register the given directory with the WatchService
     */
    private void register(Path dir) throws IOException {
    	
        WatchKey key = dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
        
        if (trace) {
        	
            Path prev = keys.get(key);
            
            if (prev == null) {
            	
                System.out.format("register: %s\n", dir);
                
            } else {
            	
                if (!dir.equals(prev)) {
                	
                    System.out.format("update: %s -> %s\n", prev, dir);
                    
                }
            }
        }
        keys.put(key, dir);
    }

    /**
     * Register the given directory, and all its sub-directories, with the
     * WatchService.
     */
    private void registerAll(final Path start) throws IOException {
        // register directory and sub-directories
        Files.walkFileTree(start, new SimpleFileVisitor<Path>() {
            @Override
            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
                throws IOException
            {
                register(dir);
                return FileVisitResult.CONTINUE;
            }
        });
    }

    /**
     * Creates a WatchService and registers the given directory
     */
    WatchDir(Path dir, boolean recursive) throws IOException {
    	
        this.watcher = FileSystems.getDefault().newWatchService();
        
        this.keys = new HashMap<WatchKey,Path>();
        
        this.recursive = recursive;

        if (recursive) {
        	
            System.out.format("Scanning %s ...\n", dir);
            
            registerAll(dir);
            
            System.out.println("Done.");
            
        } else {
        	
            register(dir);
            
        }

        // enable trace after initial registration
        this.trace = true;
    }

    /**
     * Process all events for keys queued to the watcher
     */
    void processEvents() {
    	
        for (;;) {//这是个死循环

            // wait for key to be signalled
            WatchKey key;
            
            try {
                key = watcher.take();
            } catch (InterruptedException x) {
                return;
            }

            Path dir = keys.get(key);
            
            if (dir == null) {
            	
                System.err.println("WatchKey not recognized!!");
                
                continue;
            }

            for (WatchEvent<?> event: key.pollEvents()) {
            	
                WatchEvent.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();
                Path child = dir.resolve(name);

                // print out event
                System.out.format("%s: %s\n", event.kind().name(), child);

                // if directory is created, and watching recursively, then
                // register it and its sub-directories
                if (recursive && (kind == ENTRY_CREATE)) {
                    try {
                        if (Files.isDirectory(child, NOFOLLOW_LINKS)) {
                            registerAll(child);
                        }
                    } catch (IOException x) {
                        // ignore to keep sample readbale
                    }
                }
            }

            // reset key and remove from set if directory no longer accessible
            boolean valid = key.reset();
            if (!valid) {
                keys.remove(key);

                // all directories are inaccessible
                if (keys.isEmpty()) {
                    break;
                }
            }
        }
    }

    static void usage() {
        System.err.println("usage: java WatchDir [-r] dir");
        System.exit(-1);
    }
/******************************************
 * 使用方法,可以传入两个参数,
 * 第一个参数表示是否循环遍历该文件夹(可以不传),
 * 第二个参数代表要监控的文件路径。
 * 
 * @param args
 * @throws IOException
 */

    public static void main(String[] args) throws IOException {
        // parse arguments
        if (args.length == 0 || args.length > 2){
        	
            usage();
            
        }
        
        boolean recursive = false;
        
        int dirArg = 0;
        
        if (args[0].equals("-r")) {
        	
            if (args.length < 2){
            	
                usage();
                
            }
            
            recursive = true;
            
            dirArg++;
            
        }

        // register directory and process its events
        Path dir = Paths.get(args[dirArg]);
        
        new WatchDir(dir, recursive).processEvents();
    }
}

输出结果:

Scanning E:\work\ftp_file\download ...
Done.
ENTRY_CREATE: E:\work\ftp_file\download\New Text Document.txt
ENTRY_DELETE: E:\work\ftp_file\download\New Text Document.txt
ENTRY_CREATE: E:\work\ftp_file\download\11.txt
ENTRY_MODIFY: E:\work\ftp_file\download\11.txt
ENTRY_MODIFY: E:\work\ftp_file\download\11.txt
 

1.6 其它博文参考

http://codingjunkie.net/java-7-watchservice/

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!