锁定老帖子 主题:Java 文件监控,实时监控文件加载
精华帖 (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,因为我开发这个实际上是准备用来做一个自己的缓存的功能的,完毕。本人欢迎骚扰,呵呵....另外,本人表示刚毕业不久,原谅我的菜,哈哈 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2012-10-19
强大....
|
|
返回顶楼 | |
发表时间:2012-10-19
用新版的common-io就可以实现,commons-io-2.4就有这样的功能。
|
|
返回顶楼 | |
发表时间:2012-10-20
整个设计思路非常好,不过我觉得对于抽象类DiskFileMonitor 的run方法里面做的事情,应该再形成一个个单独的方法,通过run来调用,还有抽象类中的路径应该让子类去填充。这样更好,主要是抽象类里面的代码给我感觉没有层次感,都写到一个run里面去了
|
|
返回顶楼 | |
发表时间:2012-10-20
java7想实现windows下的filewatch的功能,使用过,感觉确实不是很好,后来,通过JNI的方式,自己实现。楼主这种通过轮询的方式,在文件少的时候,还行,文件数量稍微多一些估计效率就很明显了。另外,common里,已有该功能的具体实现,不过,记得好像也是用轮询的方式来完成。
|
|
返回顶楼 | |
发表时间:2012-10-20
java_fei 写道 整个设计思路非常好,不过我觉得对于抽象类DiskFileMonitor 的run方法里面做的事情,应该再形成一个个单独的方法,通过run来调用,还有抽象类中的路径应该让子类去填充。这样更好,主要是抽象类里面的代码给我感觉没有层次感,都写到一个run里面去了
嗯,谢谢您的建议,以后我写代码的时候改进改进,呵呵 |
|
返回顶楼 | |
发表时间:2012-10-20
freezingsky 写道 java7想实现windows下的filewatch的功能,使用过,感觉确实不是很好,后来,通过JNI的方式,自己实现。楼主这种通过轮询的方式,在文件少的时候,还行,文件数量稍微多一些估计效率就很明显了。另外,common里,已有该功能的具体实现,不过,记得好像也是用轮询的方式来完成。
嗯,确实是轮循,当然大师级的代码跟我这个菜鸟级的代码肯定不一样,肯定是还有优化的空间的,谢谢,谢谢,呵呵...不过我没仔细研究common里面的代码是怎么样的,在公司上班的时候,技术总监跟我说,不要依赖于第三方的jar包,要自己去实现,呵呵,后来我就慢慢感觉不带第三方jar包的感觉真是爽,呵呵 |
|
返回顶楼 | |
发表时间:2012-10-20
tianqing_525 写道 用新版的common-io就可以实现,commons-io-2.4就有这样的功能。
呵呵,想不到发个帖子也可以学到东西,我本来不知道common包里面有这个功能的,呵呵 |
|
返回顶楼 | |
发表时间:2012-10-20
hyneng 写道 强大....
看一下大哥您的资料和博客,您是资深人士,呵呵,在您这里蹦一个“强大”出来,我这是感觉兴奋又激动啊,呵呵 |
|
返回顶楼 | |
发表时间:2012-10-20
sziitjiang 写道 freezingsky 写道 java7想实现windows下的filewatch的功能,使用过,感觉确实不是很好,后来,通过JNI的方式,自己实现。楼主这种通过轮询的方式,在文件少的时候,还行,文件数量稍微多一些估计效率就很明显了。另外,common里,已有该功能的具体实现,不过,记得好像也是用轮询的方式来完成。
嗯,确实是轮循,当然大师级的代码跟我这个菜鸟级的代码肯定不一样,肯定是还有优化的空间的,谢谢,谢谢,呵呵...不过我没仔细研究common里面的代码是怎么样的,在公司上班的时候,技术总监跟我说,不要依赖于第三方的jar包,要自己去实现,呵呵,后来我就慢慢感觉不带第三方jar包的感觉真是爽,呵呵 这种人也能当上技术总监,他干嘛不直接写个spring,struts,hibernate |
|
返回顶楼 | |