Java WatchService监视未终止

时间:2014-12-13 23:12:55

标签: java eclipse watchservice

我正在开发一个在Linux Mint下使用Java watchservice(Java 8)的应用程序。我遇到的一个有趣问题是用完了inotify手表。

我正在Eclipse下开发,行为如下:

当应用程序启动时,它会递归目录结构,对每个找到的目录进行监视。当前的测试用例使用13,660个路径。我的最大值设置为16384。

如果我停止并重启应用程序几次(20次以上),它似乎正常运行。然而,最终,我将得到一系列系统错误,表明已达到最大手表数量。但是,如果我重新启动Eclipse,问题就会消失。

显然,Watch Service并没有释放它所有的资源,但是它所获得的13,660块手表中,只保留了少量(我猜不到一百只)。除非我关闭Eclipse的Java实例并重新启动它,否则它们似乎不会被释放。

为了解决这个问题,我确保在应用程序关闭并取消监视服务任务时调用监视服务的close方法。

我做的另一件事就是我为两个不同的目的运行两个独立的监视服务。我被告知你不应该运行多个,也许这就是问题所在,但如果我能帮忙,我宁愿不运行一个监视服务。

那就是说,对于我如何能够确定这个错误的原因有什么想法或建议吗?


为大量代码发布道歉。这是我对WatchService类的实现。

一些注意事项:

pathFinder在一个单独的线程中运行,它只是一个文件访问者 - 遍历目录树并返回找到的所有目录/文件的路径。

只有在将更改发布到pathsChanged属性(来自pathFinder的onSucceeded回调)时才会调用注册。

pathsChanged属性始终由setAll()调用更新。它只发布最新的更改,并不是累积的。除了watchservice,其他类会听取这些属性并做出相应的响应。

public final class LocalWatchService extends BaseTask {

private final static String TAG  = "LocalWatchService";
//watch service task
private WatchService watcher;

//path finding task and associated executor
private LocalPathFinder finder;

//root path where the watch service begins 
private final Path mRootPath;

private final ExecutorService pathFinderExecutor = 
                                    createExecutor ("pathFinder", false);

//class hash map which keys watched paths to generated watch keys
private final Map<WatchKey, Path> keys = new HashMap<WatchKey, Path>();

//reference to model property of watched paths.
private final SimpleListProperty <SyncPath> mChangedPaths = 
        new SimpleListProperty <SyncPath> 
                            (FXCollections.<SyncPath> observableArrayList());

public LocalWatchService (String rootPath) {

    super ();

    mRootPath = Paths.get(rootPath);

    //create the watch service
    try {
        this.watcher = FileSystems.getDefault().newWatchService();
    } catch (IOException e) {
        e.printStackTrace();
    }

    setOnCancelled(new EventHandler() {

        @Override
        public void handle(Event arg0) {
            pathFinderExecutor.shutdown();
        }
    });

    mChangedPaths.addListener(new ListChangeListener <SyncPath> (){

        @Override
        public void onChanged( 
            javafx.collections.ListChangeListener.Change<? extends SyncPath> 
                            arg0) {

                for (SyncPath path: arg0.getList()) {

                    //call register only when a directory is found
                    if (path.getFile() == null) {
                        try {
                            register (path.getPath());
                        } catch (IOException e) {
                            e.printStackTrace();
                        } catch (InterruptedException e) {
                            Thread.currentThread().interrupt();
                        }
                    }
                }
            }
        });
};

public SimpleListProperty<SyncPath> changedPaths() { return mChangedPaths; }

public void initializeWatchPaths() {

    ArrayList <Path> paths = new ArrayList <Path> ();

    //create a DirectoryStream filter that finds only directories
    //and symlinks

    DirectoryStream.Filter<Path> filter = 
        new DirectoryStream.Filter<Path>() {

            public boolean accept(Path file) throws IOException {

                return (Files.isDirectory(file) || 
                        Files.isSymbolicLink(file));
            }
        };

    //apply the filter to a directory stream opened on the root path
    //and save everything returned.
    paths.addAll(utils.getFiles(mRootPath, filter));

    runPathFinder (paths);
}

private void runPathFinder (ArrayList <Path> paths) {

    //need to add blocking code / mechanism in case a path finder is 
    //currently running (rare case)

    finder = new LocalPathFinder();
    finder.setPaths (paths);

    //callbacks on successful completion of pathfinder

    EventHandler <WorkerStateEvent> eh = 
        new EventHandler <WorkerStateEvent> () {

            ArrayList <SyncPath> paths = new ArrayList <SyncPath>();

            @Override
            public void handle(WorkerStateEvent arg0) {
                    for (Path p: finder.getPaths()) {
                        paths.add(
                            new SyncPath(mRootPath, p, SyncType.SYNC_NONE));
                    }

                addPaths(paths);
            }

        };

    finder.setOnSucceeded(eh);

    pathFinderExecutor.execute (finder);        
}

private void addPath(Path path, SyncType syncType) {
    mChangedPaths.setAll(new SyncPath(mRootPath, path, syncType));
}

private void addPaths(ArrayList<SyncPath> paths) {
    mChangedPaths.setAll(paths);
}

/**
 * Register the given directory with the WatchService
 * @throws InterruptedException 
 */
public final void register(Path dir) 
                                throws IOException, InterruptedException {

    //register the key with the watch service
    WatchKey key = 
        dir.register (watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);


    if (!keys.isEmpty()) {

        Path prev = keys.get(key);

        if (prev == null) {
            //This is a new key
        }
        else if (!dir.equals(prev)) {
            //This is an update
        }
    }

    keys.put(key, dir);
}

private void processWatchEvent (WatchKey key, Path dir) throws IOException, InterruptedException {

    for (WatchEvent<?> event: key.pollEvents()) {

        WatchEvent.Kind kind = event.kind();        

        // TBD - provide example of how OVERFLOW event is handled
        if (kind == OVERFLOW) {
            System.out.println ("Overflow encountered");
        }

        WatchEvent<Path> ev = (WatchEvent<Path>)event;
        Path target = dir.resolve(ev.context());

        if (kind == ENTRY_DELETE) {

            ArrayList <Path> finderList = new ArrayList <Path> ();

            if (Files.isDirectory(target)) {
                //directory deletion is not implemented apart from
                //file deletion
            }
            else
                addPath (target, SyncType.SYNC_DELETE);

        } else if (kind == ENTRY_CREATE) {

            /*
             * Added paths are passed to the pathfinder service for
             * subdirectory discovery.  Path and subpaths are then added
             * to the AddedPaths property via an event listener on
             * service's onSucceeded() event.
             * 
             * Added files are added directly to the AddedPaths property
             */

            ArrayList <Path> finderList = new ArrayList <Path> ();

            if (Files.isDirectory(target)) {
                finderList.add (target);
                runPathFinder (finderList);
            }
            //add files directly to the addedPaths property
            else {

                //a newly created file may not be immediately readable
                if (Files.isReadable(target)) {
                        addPath (target, SyncType.SYNC_CREATE);
                }
                else
                    System.err.println ("File " + target + " cannot be read");
            }

        } else if (kind == ENTRY_MODIFY) {
            System.out.println ("File modified: " + target.toString());
        }
        boolean valid = key.reset();

        if (!valid)
            break;
    }
}

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

@Override
protected Void call () throws IOException, InterruptedException {

boolean interrupted = false;

register (mRootPath);
initializeWatchPaths();

try {
    // enter watch cycle
    while (!interrupted) {

         //watch for a key change.  Thread blocks until a change occurs
        WatchKey key = null;
        interrupted = isCancelled();

         //thread blocks until a key change occurs 
         // (whether a new path is processed by finder or a watched item changes otherwise)

        try {
            key = watcher.take();
        } catch (InterruptedException e) {
            interrupted = true;
            try {
                watcher.close();
            } catch (IOException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
            // fall through and retry
        }

        Path dir = keys.get (key);

        if (dir == null) {
           System.out.println ("Null directory key encountered.");
            continue;
        }

        //process key change once it occurs
        processWatchEvent(key, dir);

        // reset key and remove from set if directory no longer accessible
        if (!key.reset()) {

            keys.remove(key);

            // all directories are inaccessible
            if (keys.isEmpty())
                break;
        }
    }
} finally {
    if (interrupted)
        Thread.currentThread().interrupt();
}
    keys.clear();
    watcher.close();
    return null;
};

}

0 个答案:

没有答案