package com.wangyin.ak47.common; import java.io.File; import java.io.IOException; import java.util.Enumeration; import java.util.LinkedList; import java.util.List; import java.util.zip.ZipEntry; import java.util.zip.ZipException; import java.util.zip.ZipFile; /** * Class-Find related helper class. * * Class查找相关辅助类 * * @author hannyu * */ public class ClassFinder { private static final Logger log = new Logger(ClassFinder.class); /** * Find out the filtered class-list, in a single jar, * or multiple jars in some directory. * * 对某个jar文件,或某个目录下的全部jar文件(包括子目录)搜索,找出满足条件的Class列表 * * @param jarPathname a single jar path, or a directory which has many jars. * @param filter filter * @return list of class * @throws IOException * @throws ZipException */ public static List<Class<?>> findClassesInJars(String jarPathname, ClassFinderFilter filter) throws ZipException, IOException{ List<Class<?>> classlist = new LinkedList<Class<?>>(); File rootfile = new File(jarPathname); if( rootfile != null && rootfile.exists() && rootfile.canRead() ){ if( rootfile.isDirectory() ){ String[] childfilenames = rootfile.list(); for(String filename : childfilenames){ File file = new File(rootfile, filename); if( filename.endsWith(".jar") ){ if( file.isFile() && file.canRead() ){ findClassesInJar(file, classlist, filter); }else{ log.warn("File[{}] is NOT a file or NO permission to read.", filename); } }else if( file.isDirectory() ){ // 目录,深度遍历 classlist.addAll( findClassesInJars(file.getAbsolutePath(), filter) ); } } }else{ findClassesInJar(rootfile, classlist, filter); } }else{ log.warn("File[{}] NOT exist or NO permission to read.", jarPathname); } return classlist; } private static void findClassesInJar(File jarfile, List<Class<?>> classlist, ClassFinderFilter filter) throws IOException{ ZipFile zipfile = new ZipFile(jarfile); Enumeration<? extends ZipEntry> ens = zipfile.entries(); while( ens.hasMoreElements() ){ ZipEntry zipen = ens.nextElement(); String name = zipen.getName(); if( name.endsWith(".class") ){ String className = jarElemName2ClassName(name); try { Class<?> clazz = Class.forName(className); if( filter.accept(clazz) ){ classlist.add(clazz); } } catch (ClassNotFoundException e) { log.warn("ClassNotFoundException when Class.forName({}) in jar[{}].", className, jarfile); } catch (NoClassDefFoundError e) { log.warn("NoClassDefFoundError when Class.forName({}) in jar[{}].", className, jarfile); } catch (Throwable e){ log.warn("{} when Class.forName({}) in jar[{}].", e.toString(), className, jarfile); } } }//while zipfile.close(); } private static String jarElemName2ClassName(String elemName){ String className = elemName.replace('\\', '.').replace('/', '.'); className = className.substring(0, className.length() - ".class".length()); return className; } /** * These instances are used to filter {@code Class<?>} in ClassFinder. * * @author hannyu * */ public static interface ClassFinderFilter { public boolean accept(Class<?> clazz); } }