论坛首页 Java企业应用论坛

Java 文件监控,实时监控文件加载

浏览 19619 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2012-10-19  

前段时间设计一个文件自动加载系统模块,功能就像是在Tomcat的webapps目录下发布war包那样,只要一有war包加载进来,tomcat立刻就会去解压这个war包,并把它挂载在tomcat上,供网络访问。

 

我这个程序的思路也正是这样,可以监控某一目录下的文件,包括文件的增加,删除,修改,正在加载,加载成功等事件,使用抽象类和接口方式来编写,要拓展此类,十分容易,当然这个也只是个人粗制滥造的一个小玩意,大有优化的空间。在JDK7.0中包含了文件监控的API,不过听说性能很不行,事件触发很不稳定,看来oracle收购了sun之后,Java前途一片渺茫啊....

 

先说说我设计的思路:启动一个不断循环的守护线程,不断检测某目录下的文件列表,并将这些文件名称和文件MD5校验码缓存起来,在下一个循环的时候直接从缓存中取出数据,进行对比,如果发现MD5校验不一样,说明文件被更新,还有文件增加,删除的事件,在代码中有相关的注释,下面开始贴代码

 

1,Monitor,这个是一个接口,开始时是设计用来做各种监控的,例如文件监控,内存监控,CPU监控,网络监控等,Monitor接口没有方法,只是一个接口标识

package cn.std.run.monitor;

public interface Monitor{

}

 

2,FileMonitor,这是一个文件监控的接口,继承Monitor,定义了文件监控的几个方法,所有有关文件监控的实现类,都必须实现此接口

package cn.std.run.monitor;


public interface FileMonitor extends Monitor ,Runnable{

	/** 文件删除 */
	void deleteAction(Object fileName);
	/** 文件增加 */
	void addAction(Object fileName);
	/** 文件更新 */
	void updateAction(Object fileName);
	/** 正在加载 */
	void isLoading(Object fileName);
	/** 加载成功 */
	void loadSuccess(Object fileName);
}

 

3,DiskFileMonitor,这是一个抽象类,实现FileMonitor接口,并对FileMonitor做了相关实现和拓展,提供给程序员后续开发的接口,程序员如果需要开发有关文件监控的相关功能,直接继承DiskFileMonitor类,就搞定

package cn.std.run.monitor;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;

public abstract class DiskFileMonitor implements FileMonitor{

	private final String ServiceFilesKey = "ServiceFiles";
	private final String ServiceFilesMapKey = "ServiceFilesMapKey";
	private String filePath = "./src/cn/std/pool/cache";
	
	public DiskFileMonitor(String fpath) {
		try {
			filePath  = fpath;
			Vector<String> files = new Vector<String>();
			getFiles(files, fpath);
			CacheMgr.putCache(ServiceFilesKey,files);
			Map<String,String> hm = new HashMap<String,String>(files.size());
			for(String f:files){
				String fp = fpath+"/"+f;
				fp = fp.replaceAll("//", "/");
				try {
					String hash = HashFile.getHash(fp, "MD5");
					hm.put(f, hash);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			CacheMgr.putCache(ServiceFilesMapKey, hm);
			CacheMgr.lsCache();
			//Vector ve = (Vector)CacheMgr.getCache(ServiceFilesKey);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	/** 递归获取目录下的所有文件 */
	private static void getFiles(Vector<String> ret,String fpath) throws IOException { 
        File dir = new File(fpath); 
        File[] files = dir.listFiles(); 
        
        if (files == null) 
            return; 
        for (int i = 0; i < files.length; i++) { 
            if (files[i].isDirectory()) { 
            	getFiles(ret,files[i].getAbsolutePath()); 
            } else { 
                String fileName = files[i].getName();
                ret.addElement(fileName);
            } 
        } 
    }
	
	@Override
	@SuppressWarnings("unchecked")
	public void run() {
		try {
			Vector<String> notLoadFiles = new Vector<String>();
			while(true){
				Vector<String> diskfiles = new Vector<String>();
				getFiles(diskfiles, filePath);//从磁盘中读出文件
				//判断文件是否已经加载
				Object obj = CacheMgr.getCache(ServiceFilesKey);
				Vector<String> cachefiles = null;
				if(obj instanceof Vector){
					cachefiles = (Vector<String>)obj;
				}
				if(null != cachefiles){
					int diskfilesSize = diskfiles.size();
					int cachefilesSize = cachefiles.size();
					
					//磁盘文件>缓存文件,说明磁盘中增加了文件
					if(diskfilesSize > cachefilesSize){
						Enumeration<String> diskEn = diskfiles.elements();
						while(diskEn.hasMoreElements()){
							Object diskElement = diskEn.nextElement();
							if(cachefiles.contains(diskElement)){//如果缓存中已经包含了磁盘元素,进行下一个循环
								continue;
							}else{
								notLoadFiles.addElement((String)diskElement);
							}
						}//end while 
					//磁盘中删除了文件
					}else if(diskfilesSize < cachefilesSize){
						Enumeration<String> cacheEn = cachefiles.elements();
						while(cacheEn.hasMoreElements()){
							Object cacheElement = cacheEn.nextElement();
							if(diskfiles.contains(cacheElement)){//如果缓存中已经包含了磁盘元素,进行下一个循环
								continue;
							}else{
								cachefiles.removeElement(cacheElement);
							//	System.out.println(cacheElement+" 文件删除");
								deleteAction(cacheElement);
								HashMap<String,String> upmap = (HashMap<String,String>)CacheMgr.getCache(ServiceFilesMapKey);
								upmap.remove(cacheElement);
							}
						}//end while 
					//文件数量没有变化,分两种情况,1,删除N个文件同时又增加了N个文件。2,文件原封不动
					}else {
						Map<String,String> hm = (Map<String, String>) CacheMgr.getCache(ServiceFilesMapKey);
						Enumeration<String> diskEn = diskfiles.elements();
						Vector<Object> isIn = new Vector<Object>(diskfilesSize);
						while(diskEn.hasMoreElements()){
							Object diskElement = diskEn.nextElement();
							String diskfilepath = filePath+"/"+diskElement;
							diskfilepath = diskfilepath.replace("//", "/");
							String newhash = HashFile.getHash(diskfilepath, "MD5");
							String mapHash = hm.get(diskElement);
							if(null != mapHash){//如果不为空,说明有这个文件
								isIn.addElement(diskElement);
								if(mapHash.equals(newhash)){
									continue;
								}else{
									updateAction(diskElement);
									//更新文件hash
									HashMap<String,String> upmap = (HashMap<String,String>)CacheMgr.getCache(ServiceFilesMapKey);
									upmap.put(diskElement.toString(),newhash);
								}
							}else{//如果为空,说明重命名了一个文件
							//	deleteAction(diskElement);
								addAction(diskElement);
								hm.put(diskElement.toString(), newhash);
								cachefiles.addElement(diskElement.toString());
								isIn.addElement(diskElement);
							} 
						}//end while
						
						List<String> willRemove = new ArrayList<String>(hm.size());
						//遍历已经存在的元素,找出被重命名的元素
						for(Map.Entry<String, String> m:hm.entrySet()){
							if(isIn.contains(m.getKey())){
								continue;
							}else{
								willRemove.add(m.getKey());
							}
						}
						
						for(String element:willRemove){
							hm.remove(element);
							cachefiles.removeElement(element);
							deleteAction(element);
						}
					}
					
					if(notLoadFiles.size() == 0){
						//服务文件没有更新
					}else{
						//服务文件更新
						
						Vector<String> loadedfiles = new Vector<String>();
						//此处加载服务文件
						for(String name:notLoadFiles){
					//		System.out.println(name+" 未加载");
							addAction(name);
					//		System.out.println(name+" 正在加载..");
							loadedfiles.addElement(name);
							isLoading(name);
					//		System.out.println(name+" 加载成功");
							cachefiles.addElement(name);
							HashMap<String, String> hm = (HashMap<String,String>)CacheMgr.getCache(ServiceFilesMapKey);
							String path = filePath+"/"+name;
							path = path.replace("//", "/");
							hm.put(name,HashFile.getHash(path, "MD5"));
							loadSuccess(name);
							CacheMgr.lsCache();
						}
						notLoadFiles.removeAll(loadedfiles);
					}
				}
				Thread.sleep(500);
			}//end while(true)
		} catch (IOException e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
}

 

4,MyDiskFileMonitor,这是一个测试类,程序启动之后直接监控指定目录,指定目录下如果有文件变化,MyDiskFileMonitor会马上得到调用,例如,如果增加文件,AddAction方法会被调用,参数是新增加的文件名

package cn.std.run.monitor;

public class MyDiskFileMonitor extends DiskFileMonitor{

	public MyDiskFileMonitor(String fpath) {
		super(fpath);
	}

	@Override
	public void deleteAction(Object fileName) {
		System.out.println(fileName +" 元素删除");
	}

	@Override
	public void addAction(Object fileName) {
		System.out.println(fileName +" 新增元素");
		
	}

	@Override
	public void updateAction(Object fileName) {
		System.out.println(fileName +" 元素更新");
		
	}


	@Override
	public void isLoading(Object fileName) {
		System.out.println(fileName +" 正在加载");
	}

	@Override
	public void loadSuccess(Object fileName) {
		System.out.println(fileName +" 加载成功");
	}
	
	public static void main(String[] args) {
		String filePath = "F:/monitor";
		MyDiskFileMonitor mo = new MyDiskFileMonitor(filePath);
		new Thread(mo).start();
	}
}

 

好了,运行起来看看结果:


另外,附上我自己封装的一个用来保存数据的类,暂定义是Cache,因为我开发这个实际上是准备用来做一个自己的缓存的功能的,完毕。本人欢迎骚扰,呵呵....另外,本人表示刚毕业不久,原谅我的菜,哈哈

  • 大小: 6.8 KB
   发表时间:2012-10-19  
强大....
0 请登录后投票
   发表时间:2012-10-19  
用新版的common-io就可以实现,commons-io-2.4就有这样的功能。
0 请登录后投票
   发表时间:2012-10-20  
整个设计思路非常好,不过我觉得对于抽象类DiskFileMonitor 的run方法里面做的事情,应该再形成一个个单独的方法,通过run来调用,还有抽象类中的路径应该让子类去填充。这样更好,主要是抽象类里面的代码给我感觉没有层次感,都写到一个run里面去了
1 请登录后投票
   发表时间:2012-10-20  
java7想实现windows下的filewatch的功能,使用过,感觉确实不是很好,后来,通过JNI的方式,自己实现。楼主这种通过轮询的方式,在文件少的时候,还行,文件数量稍微多一些估计效率就很明显了。另外,common里,已有该功能的具体实现,不过,记得好像也是用轮询的方式来完成。
0 请登录后投票
   发表时间:2012-10-20  
java_fei 写道
整个设计思路非常好,不过我觉得对于抽象类DiskFileMonitor 的run方法里面做的事情,应该再形成一个个单独的方法,通过run来调用,还有抽象类中的路径应该让子类去填充。这样更好,主要是抽象类里面的代码给我感觉没有层次感,都写到一个run里面去了

嗯,谢谢您的建议,以后我写代码的时候改进改进,呵呵
0 请登录后投票
   发表时间:2012-10-20  
freezingsky 写道
java7想实现windows下的filewatch的功能,使用过,感觉确实不是很好,后来,通过JNI的方式,自己实现。楼主这种通过轮询的方式,在文件少的时候,还行,文件数量稍微多一些估计效率就很明显了。另外,common里,已有该功能的具体实现,不过,记得好像也是用轮询的方式来完成。

嗯,确实是轮循,当然大师级的代码跟我这个菜鸟级的代码肯定不一样,肯定是还有优化的空间的,谢谢,谢谢,呵呵...不过我没仔细研究common里面的代码是怎么样的,在公司上班的时候,技术总监跟我说,不要依赖于第三方的jar包,要自己去实现,呵呵,后来我就慢慢感觉不带第三方jar包的感觉真是爽,呵呵
0 请登录后投票
   发表时间:2012-10-20  
tianqing_525 写道
用新版的common-io就可以实现,commons-io-2.4就有这样的功能。

呵呵,想不到发个帖子也可以学到东西,我本来不知道common包里面有这个功能的,呵呵
0 请登录后投票
   发表时间:2012-10-20  
hyneng 写道
强大....

看一下大哥您的资料和博客,您是资深人士,呵呵,在您这里蹦一个“强大”出来,我这是感觉兴奋又激动啊,呵呵
0 请登录后投票
   发表时间:2012-10-20  
sziitjiang 写道
freezingsky 写道
java7想实现windows下的filewatch的功能,使用过,感觉确实不是很好,后来,通过JNI的方式,自己实现。楼主这种通过轮询的方式,在文件少的时候,还行,文件数量稍微多一些估计效率就很明显了。另外,common里,已有该功能的具体实现,不过,记得好像也是用轮询的方式来完成。

嗯,确实是轮循,当然大师级的代码跟我这个菜鸟级的代码肯定不一样,肯定是还有优化的空间的,谢谢,谢谢,呵呵...不过我没仔细研究common里面的代码是怎么样的,在公司上班的时候,技术总监跟我说,不要依赖于第三方的jar包,要自己去实现,呵呵,后来我就慢慢感觉不带第三方jar包的感觉真是爽,呵呵


这种人也能当上技术总监,他干嘛不直接写个spring,struts,hibernate
1 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics