开发web应用时,有时更新了类却没有生效,其实是因为jboss/tomcat中其他发布包下有同名类(包括全路径都相同)。
于是萌发了做个程序来检查指定目录是否存在重复类(通过asm从类文件中取类的全路径),扩展开来,还支持查找重复的文件(按文件md5进行比较),重复的jar文件。
主要代码如下:
package cn.jerryhouse.util.dup_files; import java.io.File; public abstract class FileProcessor { private long totalFileCount = 0; private long processedFileCount = 0; public void processFiles(File[] dirs) throws Exception { for (File file : dirs) { processFile(file); } } public void processFile(File file) throws Exception { if (file.isFile()) { if (isFileAccepted(file)) { handleFile(file); processedFileCount++; } totalFileCount++; } else { File[] files = file.listFiles(); for (File fileInDir : files) { processFile(fileInDir); } } } protected boolean isFileAccepted(File file) throws Exception { return true; } protected abstract void handleFile(File file); public long getTotalFileCount() { return totalFileCount; } public long getProcessedFileCount() { return processedFileCount; } }
package cn.jerryhouse.util.dup_files; import java.io.File; import java.io.IOException; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; public class FileDupFinder extends FileProcessor { protected Map<String, List<String>> fileMap = new ConcurrentHashMap<String, List<String>>(); public void analyseResult() { boolean foundDup = false; for (Entry<String, List<String>> entry : fileMap.entrySet()) { List<String> classPathList = entry.getValue(); if (classPathList.size() > 1) { System.out.println(" paths:" + classPathList); foundDup = true; } } if (foundDup == false) { System.out.println("No duplicate file found."); } } protected void handleFile(final File file) { try { String fileMD5 = FileDigest.getFileMD5(file); try { List<String> list; if (fileMap.get(fileMD5) != null) { list = fileMap.get(fileMD5); list.add(file.getCanonicalPath()); } else { list = new LinkedList<String>(); list.add(file.getCanonicalPath()); fileMap.put(fileMD5, list); } } catch (IOException e) { e.printStackTrace(); } } catch (Exception e) { e.printStackTrace(); } } }
package cn.jerryhouse.util.dup_files; import java.io.File; public class JarFileDupFinder extends FileDupFinder { protected boolean isFileAccepted(final File file) throws Exception { if(file.getCanonicalPath().endsWith(".jar")) { return true; } else { return false; } } }
package cn.jerryhouse.util.dup_files; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.LinkedList; import java.util.List; import java.util.Map.Entry; import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.Attribute; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.MethodVisitor; public class ClassDupFinder extends FileDupFinder { protected boolean isFileAccepted(final File file) throws Exception { if (file.getCanonicalPath().endsWith(".class")) { return true; } else { return false; } } public void analyseResult() { boolean foundDup = false; for (Entry<String, List<String>> entry : fileMap.entrySet()) { List<String> classPathList = entry.getValue(); if (classPathList.size() > 1) { System.out.println("class:" + entry.getKey() + " paths:" + classPathList); foundDup = true; } } if (foundDup == false) { System.out.println("No duplicate class found."); } } protected void handleFile(final File file) { try { String filePath = file.getCanonicalPath(); ClassVisitor visitor = new ClassVisitor() { @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { try { List<String> list; if (fileMap.get(name) != null) { list = fileMap.get(name); list.add(file.getCanonicalPath()); } else { list = new LinkedList<String>(); list.add(file.getCanonicalPath()); fileMap.put(name, list); } } catch (IOException e) { e.printStackTrace(); } } @Override public void visitSource(String source, String debug) { } @Override public void visitOuterClass(String owner, String name, String desc) { } @Override public AnnotationVisitor visitAnnotation(String desc, boolean visiable) { return null; } @Override public void visitAttribute(Attribute attr) { } @Override public void visitInnerClass(String name, String outerName, String innerName, int access) { } @Override public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { return null; } @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { return null; } @Override public void visitEnd() { } }; ClassReader cr = new ClassReader(new FileInputStream(filePath)); cr.accept(visitor, 0); } catch (Exception e) { e.printStackTrace(); } } }
package cn.jerryhouse.util.dup_files; import java.io.File; import java.io.FileInputStream; import java.math.BigInteger; import java.security.MessageDigest; import java.util.HashMap; import java.util.Map; public class FileDigest { /** * 获取单个文件的MD5值 * @param file */ public static String getFileMD5(File file) { if (!file.isFile()) { return null; } MessageDigest digest = null; FileInputStream in = null; byte buffer[] = new byte[1024]; int len; try { digest = MessageDigest.getInstance("MD5"); in = new FileInputStream(file); while ((len = in.read(buffer, 0, 1024)) != -1) { digest.update(buffer, 0, len); } in.close(); } catch (Exception e) { e.printStackTrace(); return null; } BigInteger bigInt = new BigInteger(1, digest.digest()); return bigInt.toString(16); } /** * 获取文件夹中文件的MD5值 * @param file * @param listChild 为true时,递归子目录中的文件;否则不递归 */ public static Map<String, String> getDirMD5(File file, boolean listChild) { if (!file.isDirectory()) { return null; } Map<String, String> map = new HashMap<String, String>(); String md5; File files[] = file.listFiles(); for (int i = 0; i < files.length; i++) { File f = files[i]; if (f.isDirectory() && listChild) { map.putAll(getDirMD5(f, listChild)); } else { md5 = getFileMD5(f); if (md5 != null) { map.put(f.getPath(), md5); } } } return map; } }
package cn.jerryhouse.util.dup_files; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; public abstract class LineReaderProcessor extends FileProcessor { private long totalFileCount = 0; private long processedFileCount = 0; public void processFiles(File[] dirs) throws Exception { for (File file : dirs) { processFile(file); } } public void processFile(File file) throws Exception { if (file.isFile()) { if (isFileAccepted(file)) { handleFile(file); } } else { File[] files = file.listFiles(); for (File fileInDir : files) { processFile(fileInDir); } } } protected boolean isFileAccepted(File file) throws Exception { return true; } protected void handleFile(File file) { try { BufferedReader br = new BufferedReader(new FileReader(file)); String line; while((line=br.readLine())!=null) { readLine(file,line); } br.close(); } catch (Exception e) { e.printStackTrace(); } } protected abstract void readLine(File file,String line); public long getTotalFileCount() { return totalFileCount; } public long getProcessedFileCount() { return processedFileCount; } }
package cn.jerryhouse.util.dup_files; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.util.UUID; public abstract class LineUpdateProcessor extends FileProcessor { private long totalFileCount = 0; private long processedFileCount = 0; private String NEW_LINE = System.getProperty("line.separator"); public void processFiles(File[] dirs) throws Exception { for (File file : dirs) { processFile(file); } } public void processFile(File file) throws Exception { if (file.isFile()) { if (isFileAccepted(file)) { handleFile(file); } } else { File[] files = file.listFiles(); for (File fileInDir : files) { processFile(fileInDir); } } } protected boolean isFileAccepted(File file) throws Exception { return true; } protected void handleFile(File file) { try { BufferedReader br = new BufferedReader(new FileReader(file)); File tmpFile = new File(tmpFilePath(file)); BufferedWriter bw = new BufferedWriter(new FileWriter(tmpFile)); String line; while((line=br.readLine())!=null) { String updatedLine = updateLine(file,line); bw.write(updatedLine+NEW_LINE); } br.close(); bw.close(); file.delete(); tmpFile.renameTo(file); } catch (Exception e) { e.printStackTrace(); } } private String tmpFilePath(File file) { String dir = file.getParent(); String filePath = dir+""+getUniqFileName(); return filePath; } private String getUniqFileName() { return UUID.randomUUID().toString(); } protected abstract String updateLine(File file,String line); public long getTotalFileCount() { return totalFileCount; } public long getProcessedFileCount() { return processedFileCount; } }
简单测试代码:
package cn.jerryhouse.util.dup_files.test; import java.io.File; import org.junit.Test; import cn.jerryhouse.util.dup_files.ClassDupFinder; import cn.jerryhouse.util.dup_files.FileDupFinder; import cn.jerryhouse.util.dup_files.JarFileDupFinder; public class DupTest { @Test public void testJarFiles() { try { File[] files = new File[1]; files[0] = new File("E:\workspace\yinxing"); JarFileDupFinder dupFinder = new JarFileDupFinder(); dupFinder.processFiles(files); dupFinder.analyseResult(); } catch (Exception e) { e.printStackTrace(); } } @Test public void testFileDup() { try { File[] files = new File[1]; files[0] = new File("E:\workspace\yinxing"); FileDupFinder classDupFinder = new FileDupFinder(); classDupFinder.processFiles(files); classDupFinder.analyseResult(); } catch (Exception e) { e.printStackTrace(); } } @Test public void testClassDup() { try { File[] files = new File[1]; files[0] = new File("E:\workspace\yinxing"); ClassDupFinder classDupFinder = new ClassDupFinder(); classDupFinder.processFiles(files); classDupFinder.analyseResult(); } catch (Exception e) { e.printStackTrace(); } } }
注:依赖jar包asm.jar。