Package com.sun.jna.platform.win32

Source Code of com.sun.jna.platform.win32.W32FileMonitor

/* Copyright (c) 2007 Timothy Wall, All Rights Reserved
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*/
package com.sun.jna.platform.win32;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import com.sun.jna.platform.FileMonitor;
import com.sun.jna.platform.win32.BaseTSD.ULONG_PTRByReference;
import com.sun.jna.platform.win32.WinBase.OVERLAPPED;
import com.sun.jna.platform.win32.WinNT.FILE_NOTIFY_INFORMATION;
import com.sun.jna.platform.win32.WinNT.HANDLE;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.ptr.PointerByReference;

public class W32FileMonitor extends FileMonitor {
   
    private static final int BUFFER_SIZE = 4096;
   
    private class FileInfo {
        public final File file;
        public final HANDLE handle;
        public final int notifyMask;
        public final boolean recursive;
        public final FILE_NOTIFY_INFORMATION info = new FILE_NOTIFY_INFORMATION(BUFFER_SIZE);
        public final IntByReference infoLength = new IntByReference();
        public final OVERLAPPED overlapped = new OVERLAPPED();
        public FileInfo(File f, HANDLE h, int mask, boolean recurse) {
            this.file = f;
            this.handle = h;
            this.notifyMask = mask;
            this.recursive = recurse;
        }
    }
    private Thread watcher;
    private HANDLE port;
    private final Map<File, FileInfo> fileMap = new HashMap<File, FileInfo>();
    private final Map<HANDLE, FileInfo> handleMap = new HashMap<HANDLE, FileInfo>();
    private boolean disposing = false;
   
    private void handleChanges(FileInfo finfo) throws IOException {
        Kernel32 klib = Kernel32.INSTANCE;
        FILE_NOTIFY_INFORMATION fni = finfo.info;
        // Need an explicit read, since data was filled in asynchronously
        fni.read();
        do {
            FileEvent event = null;
            File file = new File(finfo.file, fni.getFilename());
            switch(fni.Action) {
            case 0:
              break;
            case WinNT.FILE_ACTION_MODIFIED:
                event = new FileEvent(file, FILE_MODIFIED);
                break;
            case WinNT.FILE_ACTION_ADDED:
                event = new FileEvent(file, FILE_CREATED);
                break;
            case WinNT.FILE_ACTION_REMOVED:
                event = new FileEvent(file, FILE_DELETED);
                break;
            case WinNT.FILE_ACTION_RENAMED_OLD_NAME:
                event = new FileEvent(file, FILE_NAME_CHANGED_OLD);
                break;
            case WinNT.FILE_ACTION_RENAMED_NEW_NAME:
                event = new FileEvent(file, FILE_NAME_CHANGED_NEW);
                break;
            default:
                // TODO: other actions...
                System.err.println("Unrecognized file action '" + fni.Action + "'");
            }
       
            if (event != null) {
                notify(event);
            }
           
            fni = fni.next();
        } while (fni != null);
       
        // trigger the next read
        if (!finfo.file.exists()) {
            unwatch(finfo.file);
            return;
        }
       
        if (!klib.ReadDirectoryChangesW(finfo.handle, finfo.info,
            finfo.info.size(), finfo.recursive, finfo.notifyMask,
            finfo.infoLength, finfo.overlapped, null)) {         
          if (! disposing) {
            int err = klib.GetLastError();
            throw new IOException("ReadDirectoryChangesW failed on "
                                  + finfo.file + ": '"
                                  + Kernel32Util.formatMessageFromLastErrorCode(err)
                                  + "' (" + err + ")");
          }
        }
    }
   
    private FileInfo waitForChange() {
        IntByReference rcount = new IntByReference();
        ULONG_PTRByReference rkey = new ULONG_PTRByReference();
        PointerByReference roverlap = new PointerByReference();
        if (! Kernel32.INSTANCE.GetQueuedCompletionStatus(port, rcount, rkey, roverlap, WinBase.INFINITE)) {
            return null;
        }
        synchronized (this) {
            return handleMap.get(new HANDLE(rkey.getValue().toPointer()));
        }
    }
   
    private int convertMask(int mask) {
        int result = 0;
        if ((mask & FILE_CREATED) != 0) {
            result |= WinNT.FILE_NOTIFY_CHANGE_CREATION;
        }
        if ((mask & FILE_DELETED) != 0) {
            result |= WinNT.FILE_NOTIFY_CHANGE_NAME;
        }
        if ((mask & FILE_MODIFIED) != 0) {
            result |= WinNT.FILE_NOTIFY_CHANGE_LAST_WRITE;
        }
        if ((mask & FILE_RENAMED) != 0) {
            result |= WinNT.FILE_NOTIFY_CHANGE_NAME;
        }
        if ((mask & FILE_SIZE_CHANGED) != 0) {
            result |= WinNT.FILE_NOTIFY_CHANGE_SIZE;
        }
        if ((mask & FILE_ACCESSED) != 0) {
            result |= WinNT.FILE_NOTIFY_CHANGE_LAST_ACCESS;
        }
        if ((mask & FILE_ATTRIBUTES_CHANGED) != 0) {
            result |= WinNT.FILE_NOTIFY_CHANGE_ATTRIBUTES;
        }
        if ((mask & FILE_SECURITY_CHANGED) != 0) {
            result |= WinNT.FILE_NOTIFY_CHANGE_SECURITY;
        }
        return result;
    }

    private static int watcherThreadID;

    protected synchronized void watch(File file, int eventMask, boolean recursive) throws IOException {
        File dir = file;
        if (!dir.isDirectory()) {
            recursive = false;
            dir = file.getParentFile();
        }
        while (dir != null && !dir.exists()) {
            recursive = true;
            dir = dir.getParentFile();
        }
        if (dir == null) {
            throw new FileNotFoundException("No ancestor found for " + file);
        }
        Kernel32 klib = Kernel32.INSTANCE;
        int mask = WinNT.FILE_SHARE_READ
            | WinNT.FILE_SHARE_WRITE | WinNT.FILE_SHARE_DELETE;
        int flags = WinNT.FILE_FLAG_BACKUP_SEMANTICS
            | WinNT.FILE_FLAG_OVERLAPPED;
        HANDLE handle = klib.CreateFile(file.getAbsolutePath(),
            WinNT.FILE_LIST_DIRECTORY,
            mask, null, WinNT.OPEN_EXISTING,
                flags, null);
        if (WinBase.INVALID_HANDLE_VALUE.equals(handle)) {
            throw new IOException("Unable to open " + file + " ("
                                  + klib.GetLastError() + ")");
        }
        int notifyMask = convertMask(eventMask);
        FileInfo finfo = new FileInfo(file, handle, notifyMask, recursive);
        fileMap.put(file, finfo);
        handleMap.put(handle, finfo);
        // Existing port is returned
        port = klib.CreateIoCompletionPort(handle, port, handle.getPointer(), 0);
        if (WinBase.INVALID_HANDLE_VALUE.equals(port)) {
            throw new IOException("Unable to create/use I/O Completion port "
                    + "for " + file + " ("
                    + klib.GetLastError() + ")");
        }
        // TODO: use FileIOCompletionRoutine callback method instead of a
        // dedicated thread
        if (!klib.ReadDirectoryChangesW(handle, finfo.info, finfo.info.size(),
                                        recursive, notifyMask, finfo.infoLength,
                                        finfo.overlapped, null)) {
            int err = klib.GetLastError();
            throw new IOException("ReadDirectoryChangesW failed on "
                                  + finfo.file + ", handle " + handle
                                  + ": '" + Kernel32Util.formatMessageFromLastErrorCode(err)
                                  + "' (" + err + ")");
        }
        if (watcher == null) {
            watcher = new Thread("W32 File Monitor-" + (watcherThreadID++)) {
                public void run() {
                    FileInfo finfo;
                    while (true) {
                       finfo = waitForChange();
                       if (finfo == null) {
                          synchronized(W32FileMonitor.this) {
                             if (fileMap.isEmpty()) {
                                watcher = null;
                                break;
                             }
                          }
                          continue;
                        }
                      
                        try {
                            handleChanges(finfo);
                        }
                        catch(IOException e) {
                            // TODO: how is this best handled?
                            e.printStackTrace();
                        }
                    }
                }
            };
            watcher.setDaemon(true);
            watcher.start();
        }
    }

    protected synchronized void unwatch(File file) {
        FileInfo finfo = fileMap.remove(file);
        if (finfo != null) {
            handleMap.remove(finfo.handle);
            Kernel32 klib = Kernel32.INSTANCE;
            // bug: the watcher may still be processing this file
            klib.CloseHandle(finfo.handle);
        }
    }
   
    public synchronized void dispose() {
      disposing = true;
     
        // unwatch any remaining files in map, allows watcher thread to exit
        int i = 0;
        for (Object[] keys = fileMap.keySet().toArray(); !fileMap.isEmpty();) {
            unwatch((File)keys[i++]);
        }
       
        Kernel32 klib = Kernel32.INSTANCE;
        klib.PostQueuedCompletionStatus(port, 0, null, null);
        klib.CloseHandle(port);
        port = null;
        watcher = null;
    }
}
TOP

Related Classes of com.sun.jna.platform.win32.W32FileMonitor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.