View Javadoc
1   /**
2    * Copyright (c) 2009-2010 by Glodon
3    * All rights reserved.
4    */
5   package gboat2.base.bridge.util;
6   
7   import gboat2.base.bridge.exception.DefaultGboatNestedException;
8   
9   import java.io.File;
10  import java.io.FileInputStream;
11  import java.io.FileNotFoundException;
12  import java.io.IOException;
13  import java.net.URL;
14  import java.util.ArrayList;
15  import java.util.List;
16  import java.util.zip.ZipEntry;
17  import java.util.zip.ZipInputStream;
18  
19  import org.apache.commons.io.IOUtils;
20  import org.slf4j.Logger;
21  import org.slf4j.LoggerFactory;
22  
23  
24  /**
25   * 包,类操作的辅助类
26   * 
27   * @author lysming
28   * @since 1.0
29   */
30  public abstract class PackageClassUtil {
31      
32      private static final Logger logger = LoggerFactory.getLogger(PackageClassUtil.class);
33      
34      /**
35       * 获取包中的所有 Java 类
36       * @param pkgname 包名,如:gboat2.base.bridge.util
37       * @return 包中所有 class
38       */
39      public static List<Class<?>> getClasses(String pkgname) {
40          return getClasses(pkgname, null);
41      }
42  	
43  	/**
44  	 * 获取包中的所有 Java 类
45  	 * 
46  	 * @param pkgname 包名
47  	 * @param classLoader 类加载器
48  	 * @return 返回包中所有 class
49  	 */
50  	public static List<Class<?>> getClasses(String pkgname, ClassLoader classLoader) {
51  		ArrayList<Class<?>> classes = new ArrayList<Class<?>>();
52  
53          ClassLoader cld = Thread.currentThread().getContextClassLoader();
54          if (cld == null)
55              throw new DefaultGboatNestedException("Can't get class loader.");
56  
57          String path = pkgname.replace('.', '/');
58          URL resource = cld.getResource(path);
59          if (resource == null)
60              throw new DefaultGboatNestedException("No resource for " + path);
61  
62          File directory = new File(resource.getFile());
63          String dirPath = directory.getAbsolutePath();
64          logger.debug("包 [] 对应的物理路径为:{}", pkgname, dirPath);
65  		if (directory.exists()) {
66  			// Get the list of the files contained in the package
67  			String[] filenames = directory.list();
68  			String filename = null;
69  			try {
70      			for (int i = 0; i < filenames.length; i++) {
71      			    filename = filenames[i];
72      				// we are only interested in .class files
73      				if (filename.endsWith(".class")) {
74      					// removes the .class extension
75      					classes.add(Class.forName(pkgname + '.' + filename.substring(0, filename.length() - 6)));
76      				}
77      			}
78  			}catch (ClassNotFoundException e) {
79  			    throw new DefaultGboatNestedException(filename + " does not exist in the package [" + pkgname + "]");
80              }
81  		} else if (dirPath.indexOf(".jar!") != -1) { // 从jar包中读取
82  			String zipFileName = dirPath.substring(dirPath.indexOf("file") + 6, dirPath.indexOf("!"));
83  			String prefixPath = dirPath.substring(dirPath.indexOf("!") + 2).replaceAll("\\\\", "/");
84  			ZipInputStream in = null;
85  			String name = null;
86  			try {
87  				in = new ZipInputStream(new FileInputStream(zipFileName));
88  				ZipEntry entry = null;
89  				while ((entry = in.getNextEntry()) != null) {
90  					if (!entry.isDirectory()) {
91  						name = entry.getName();
92  						if (name.startsWith(prefixPath) && name.endsWith(".class")) {
93  							classes.add(Class.forName(name.substring(0, name.indexOf(".class")).replaceAll("/", ".")));
94  						}
95  					}
96  				}
97  				   
98  			} catch (FileNotFoundException e) {
99  				throw new DefaultGboatNestedException(pkgname + " --> the jar file [" + zipFileName + "] can't find");
100 			}catch (IOException e) {
101 				throw new DefaultGboatNestedException(pkgname + " --> the jar file [" + zipFileName + "] can't read");
102 			} catch (ClassNotFoundException e) {
103 			    throw new DefaultGboatNestedException(name + " does not exist in the jar file [" + zipFileName + "]");
104             } finally {
105 			    IOUtils.closeQuietly(in); //关闭流
106 			}
107 		} else {
108 			throw new DefaultGboatNestedException(pkgname + " does not appear to be a valid package");
109 		}
110 		return classes;
111 	}
112 
113 	/** 获取 Java 类所在目录的磁盘物理路径
114 	 * @param cls Java 类
115 	 * @return Java 类所在目录的磁盘物理路径,如果传入的 Java 类是在 jar 或 war 包中,则返回对应的 jar 或 war 包所在目录的物理路径
116 	 */
117 	public static String getRealPath(Class<?> cls) {
118 		// 检查用户传入的参数是否为空
119 		if (cls == null)
120 			throw new java.lang.IllegalArgumentException("参数不能为空!");
121 		
122 		ClassLoader loader = cls.getClassLoader();
123 		// 获得类的全名,包括包名
124 		String clsName = cls.getName() + ".class";
125 		// 获得传入参数所在的包
126 		Package pack = cls.getPackage();
127 		String path = "";
128 		StringBuilder pathSB = new StringBuilder();
129 		// 如果不是匿名包,将包名转化为路径
130 		if (pack != null) {
131 			String packName = pack.getName();
132 			// 此处简单判定是否是Java基础类库,防止用户传入JDK内置的类库
133 			if (packName.startsWith("java.") || packName.startsWith("javax."))
134 				throw new java.lang.IllegalArgumentException("不要传送系统类!");
135 			
136 			// 在类的名称中,去掉包名的部分,获得类的文件名
137 			clsName = clsName.substring(packName.length() + 1);
138 			// 判定包名是否是简单包名,如果是,则直接将包名转换为路径,
139 			if (packName.indexOf(".") < 0)
140 				//path = packName + "/";
141 				pathSB.append(packName + "/");
142 			else { // 否则按照包名的组成部分,将包名转换为路径
143 				int start = 0, end = 0;
144 				end = packName.indexOf(".");
145 				while (end != -1) {
146 					//path = path + packName.substring(start, end) + "/";
147 					pathSB.append(packName.substring(start, end) + "/");
148 					start = end + 1;
149 					end = packName.indexOf(".", start);
150 				}
151 				//path = path + packName.substring(start) + "/";
152 				pathSB.append(packName.substring(start) + "/");
153 			}
154 		}
155 		path = pathSB.toString();
156 		// 调用ClassLoader的getResource方法,传入包含路径信息的类文件名
157 		java.net.URL url = loader.getResource(path + clsName);
158 		// 从URL对象中获取路径信息
159 		String realPath = url.getPath();
160 		// 去掉路径信息中的协议名"file:"
161 		int pos = realPath.indexOf("file:");
162 		if (pos > -1)
163 			realPath = realPath.substring(pos + 5);
164 		// 去掉路径信息最后包含类文件信息的部分,得到类所在的路径
165 		pos = realPath.indexOf(path + clsName);
166 		realPath = realPath.substring(0, pos - 1);
167 		// 如果类文件被打包到JAR等文件中时,去掉对应的JAR等打包文件名
168 		if (realPath.endsWith("!"))
169 			realPath = realPath.substring(0, realPath.lastIndexOf("/"));
170 		/*------------------------------------------------------------  
171 		 ClassLoader的getResource方法使用了utf-8对路径信息进行了编码,当路径  
172 		  中存在中文和空格时,他会对这些字符进行转换,这样,得到的往往不是我们想要  
173 		  的真实路径,在此,调用了URLDecoder的decode方法进行解码,以便得到原始的  
174 		  中文及空格路径  
175 		-------------------------------------------------------------*/
176 		try {
177 			realPath = java.net.URLDecoder.decode(realPath, "utf-8");
178 		} catch (Exception e) {
179 			throw new RuntimeException(e);
180 		}
181 		return realPath;
182 	}
183 }