經過Java SE 7自帶的監控服務(WatchService API)實現相似.NET FileWatcher的功能

Java SE 7 Tutorial中增長了一個監控目錄變動狀況的示例,用於介紹其新發布的WatchService API。html

 

但對於用慣了.NET FileWatcher的用戶而言,若是用於項目我認爲它有兩個欠缺:前端

一、應該提供一個獨立線程後臺運行機制,讓這個監控過程本身在後臺轉,不影響前端處理java

二、 Java不像.NET有內置的源生事件機制,不過能夠藉助它內置的Observer/Observable對象用觀察者模式實現準事件oracle

 

下面是把Java SE Tutorial示例中無關內容刪除,補充上述兩個擴展後的實現,由於這個API比較新,也但願能和你們多多探討:ide

 

一、參考.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;}
}
複製代碼

 

 二、定義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));    }}
複製代碼

 

 三、單元測試this

複製代碼
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\n", 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窗口顯示的測試內容spa

 

test5769907807190550725.txt has been ENTRY_CREATE
test4657672246246330348.txt has been ENTRY_CREATE
test1823102943601166149.txt has been ENTRY_CREATEfinished
相關文章
相關標籤/搜索