Java实现文件监控器FileMonitor

应用场景:

代码可以实现文件变化后的监听,如文件变化,自动重新加载文件内容,实现配置文件的热部署。

代码: 

package com.yx.demo.filemonitor;

import java.io.File;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

/**
 * FileMonitor
 * 文件监控器
 *
 * @author yx
 * @date 2019/12/21 0:59
 */
public class FileMonitor {

    /**
     * 每2秒更新的默认监控器
     */
    private static FileMonitor defaultFileMonitor = new FileMonitor(2 * 1000);

    private Timer timer_;
    private HashMap<File, FileEntry> files_; // File -> Long
    private List<FileEntry> fileEntrys = new java.util.concurrent.CopyOnWriteArrayList<FileEntry>();
    private Collection<WeakReference<FileListener>> listeners_; // of WeakReference(FileListener)

    private long pollingInterval = 10000;

    public static FileMonitor getDefaultFileMonitor() {
        return defaultFileMonitor;
    }

    /**
     * Create a file monitor instance with specified polling interval.
     *
     * @param pollingInterval Polling interval in milli seconds.
     */
    public FileMonitor(long pollingInterval) {
        this.pollingInterval = pollingInterval;

        files_ = new HashMap<File, FileEntry>();
        listeners_ = new ArrayList<WeakReference<FileListener>>();

        timer_ = new Timer("FileMonitor", true);
        timer_.schedule(new FileMonitorNotifier(), 0, pollingInterval);
    }

    /**
     * Stop the file monitor polling.
     */
    public void stop() {
        timer_.cancel();
        timer_ = null;
    }

    public void start() {
        if (timer_ == null) {
            timer_ = new Timer(true);
            timer_.schedule(new FileMonitorNotifier(), 0, pollingInterval);
        }
    }

    /**
     * Add file to listen for. File may be any java.io.File (including a
     * directory) and may well be a non-existing file in the case where the
     * creating of the file is to be trepped.
     * <p>
     * More than one file can be listened for. When the specified file is
     * created, modified or deleted, listeners are notified.
     *
     * @param file File to listen for.
     */
    public void addFile(String id, File file) {
        if (!files_.containsKey(file)) {

            FileEntry entry = new FileEntry(id, file, file.exists() ? file.lastModified() : -1);
            files_.put(file, entry);
        }
    }

    /**
     * 添加监控文件实体。
     *
     * @param fileEntry
     */
    public void addFileEntry(FileEntry fileEntry) {
        if (!fileEntrys.contains(fileEntry)) {
            fileEntrys.add(fileEntry);
        }
    }

    /**
     * 通过文件实体的标识判断监控文件实体是否存在。
     *
     * @param id
     * @return
     */
    public boolean fileEntryExists(String id) {
        if (id == null) {
            return false;
        }

        for (int i = 0; i < fileEntrys.size(); i++) {
            if (id.equals(fileEntrys.get(i).getId())) {
                return true;
            }
        }

        return false;
    }

    /**
     * 通过文件实体标识删除一个监控文件实体。
     *
     * @param id
     */
    public void removeFileEntry(String id) {
        if (id == null) {
            return;
        }

        for (int i = 0; i < fileEntrys.size(); i++) {
            if (id.equals(fileEntrys.get(i).getId())) {
                fileEntrys.remove(i);
                return;
            }
        }
    }

    /**
     * Remove specified file for listening.
     *
     * @param file File to remove.
     */
    public void removeFile(File file) {
        files_.remove(file);
    }

    /**
     * Add listener to this file monitor.
     *
     * @param fileListener Listener to add.
     */
    public void addListener(FileListener fileListener) {
        // Don't add if its already there
        for (Iterator<WeakReference<FileListener>> i = listeners_.iterator(); i.hasNext(); ) {
            WeakReference<FileListener> reference = i.next();
            FileListener listener = (FileListener) reference.get();
            if (listener == fileListener) {
                return;
            }
        }

        // Use WeakReference to avoid memory leak if this becomes the
        // sole reference to the object.
        listeners_.add(new WeakReference<FileListener>(fileListener));
    }

    /**
     * Remove listener from this file monitor.
     *
     * @param fileListener Listener to remove.
     */
    public void removeListener(FileListener fileListener) {
        for (Iterator<WeakReference<FileListener>> i = listeners_.iterator(); i.hasNext(); ) {
            WeakReference<FileListener> reference = (WeakReference<FileListener>) i.next();
            FileListener listener = (FileListener) reference.get();
            if (listener == fileListener) {
                i.remove();
                break;
            }
        }
    }

    /**
     * This is the timer thread which is executed every n milliseconds according
     * to the setting of the file monitor. It investigates the file in question
     * and notify listeners if changed.
     */
    private class FileMonitorNotifier extends TimerTask {
        @Override
        public void run() {
            try {
                for (Iterator<FileEntry> i = fileEntrys.iterator(); i.hasNext(); ) {
                    try {
                        FileEntry entry = i.next();
                        if (entry == null || !entry.check()) {
                            i.remove();
                        }
                    } catch (Throwable t) {
                        t.printStackTrace();
                        System.out.println("执行文件监控发生错误:" + t.getMessage());
                    }
                }

                // Loop over the registered files and see which have changed.
                // Use a copy of the list in case listener wants to alter the
                // list within its fileChanged method.
                Collection<File> files = new ArrayList<File>(files_.keySet());

                for (Iterator<File> i = files.iterator(); i.hasNext(); ) {
                    File file = i.next();
                    try {
                        FileEntry fileEntry = files_.get(file);
                        long lastModifiedTime = fileEntry.getLastModified();
                        long newModifiedTime = file.exists() ? file.lastModified() : -1;

                        //logger.debug(file.getAbsolutePath());
                        //logger.debug("    {}=>{}", lastModifiedTime, newModifiedTime);
                        // Chek if file has changed
                        if (newModifiedTime != lastModifiedTime) {
                            //logger.debug("file changed {})", file.getAbsolutePath());
                            fileEntry.setLastModified(newModifiedTime);
                            // Register new modified time
                            files_.put(file, fileEntry);

                            if (fileEntry.getFileListener() != null) {
                                fileEntry.getFileListener().fileChanged(fileEntry);
                            } else {
                                // Notify listeners
                                for (Iterator<WeakReference<FileListener>> j =
                                        listeners_.iterator(); j.hasNext(); ) {
                                    WeakReference<FileListener> reference =
                                            (WeakReference<FileListener>) j.next();
                                    FileListener listener = (FileListener) reference.get();

                                    // Remove from list if the back-end object has been GC'd
                                    if (listener == null) {
                                        j.remove();
                                    } else {
                                        listener.fileChanged(fileEntry);
                                    }
                                }
                            }
                        }
                    } catch (Throwable t) {
                        if (file != null) {
                            t.printStackTrace();
                            System.out.println(
                                    "file monitor execute error, file=" + file.getAbsolutePath() +
                                            t.getMessage());
                        } else {
                            System.out.println(
                                    "file monitor execute error, file=null" + t.getMessage());
                        }
                    }
                }
            } catch (Throwable t) {
                System.out.println("执行文件监控发生错误" + t.getMessage());
            }
        }
    }
}
package com.yx.demo.filemonitor;

/**
 * FileListener
 *
 * @author yx
 * @date 2019/12/21 0:55
 */
public interface FileListener {
	/**
	 * 
	 * @param fileEntry
	 */
	public void fileChanged(FileEntry fileEntry);
}
package com.yx.demo.filemonitor;

import java.io.File;
import java.lang.ref.WeakReference;

/**
 * FileEntry
 * 文件Entry,如果FileEntry指定了FileListener,那么当文件发生变动时只触发指定的FileListener
 *
 * @author yx
 * @date 2019/12/21 0:56
 */
public class FileEntry {

    String id;
    File file;
    long lastModified;
    FileListener fileListener = null;
    Object userData;
    WeakReference<Object> reference = null;

    /**
     * 构造函数。
     *
     * @param id
     * @param file
     */
    public FileEntry(String id, File file) {
        this(id, file, file.exists() ? file.lastModified() : -1);
    }

    public FileEntry(Object reference, String id, File file) {
        this(id, file, file.exists() ? file.lastModified() : -1);

        reference = new WeakReference<Object>(reference);
    }

    /**
     * 构造函数。
     *
     * @param id           标识
     * @param file         要监控的文件
     * @param lastmodified 最后修改日期
     */
    public FileEntry(String id, File file, long lastmodified) {
        super();
        this.id = id;
        this.file = file;
        this.lastModified = lastmodified;
    }

    public boolean check() {
        if (reference != null && reference.get() == null) {
            //监控对象已经不存在,请求FileMonitor删除自己
            return false;
        }

        long newModifiedTime = file.exists() ? file.lastModified() : -1;
        if (lastModified != newModifiedTime) {
            this.lastModified = newModifiedTime;
            FileListener ls = this.getFileListener();
            if (ls == null) {
                return false;
            } else {
                try {
                    ls.fileChanged(this);
                } catch (Exception e) {
                    e.printStackTrace();
                    System.err.println("执行文件监控事件监听" + e.getMessage());
                }

                return true;
            }
        } else {
            return true;
        }
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public File getFile() {
        return file;
    }

    public void setFile(File file) {
        this.file = file;
    }

    public long getLastModified() {
        return lastModified;
    }

    public void setLastModified(long lastModified) {
        this.lastModified = lastModified;
    }

    public FileListener getFileListener() {
        return fileListener;
    }

    public void setFileListener(FileListener fileListener) {
        this.fileListener = fileListener;
    }

    public Object getUserData() {
        return userData;
    }

    public void setUserData(Object userData) {
        this.userData = userData;
    }
}

使用demo:

// 文件路径
String fileName = "conf/database.xml";

// 文件监控
FileListener fileListener = new FileListener() {
@Override
public void fileChanged(FileEntry fileEntry) {
// TODO 文件变化后的业务处理
}
};

File file = new File(fileName);
FileEntry fileEntry = new FileEntry("database", file);
// 设置文件监控
fileEntry.setFileListener(fileListener);

FileMonitor.getDefaultFileMonitor().addFileEntry(fileEntry);
发布了142 篇原创文章 · 获赞 258 · 访问量 16万+

猜你喜欢

转载自blog.csdn.net/conconbenben/article/details/103640273