001package com.hammurapi.common; 002 003import java.io.File; 004import java.io.FileInputStream; 005import java.net.MalformedURLException; 006import java.net.URL; 007import java.util.ArrayList; 008import java.util.List; 009import java.util.Map; 010import java.util.jar.Attributes; 011import java.util.jar.Manifest; 012import java.util.logging.Logger; 013import java.util.zip.ZipEntry; 014import java.util.zip.ZipInputStream; 015 016import org.apache.bcel.classfile.Constant; 017import org.apache.bcel.classfile.ConstantClass; 018import org.apache.bcel.classfile.ConstantPool; 019import org.apache.bcel.classfile.JavaClass; 020import org.apache.bcel.util.ClassLoaderRepository; 021 022/** 023 * Scans class dependencies and returns a list of classpath entries URL's which given class depends on. 024 * This class requires BCEL 5.2 in the classpath. 025 * @author Pavel Vlasov 026 * 027 */ 028public class DependencyScanner { 029 030 private static final String CLASS_EXTENSION = ".class"; 031 032 private static final Logger logger = Logger.getLogger(DependencyScanner.class.getName()); 033 034 private static Map<ClassLoader, ClassLoaderRepository> repoCache = new java.util.HashMap<ClassLoader, ClassLoaderRepository>(); 035 036 private static URL classContainer(Class<?> clazz) { 037 String className = clazz.getName(); 038 int lastDotIdx = className.lastIndexOf('.'); 039 String shortName = lastDotIdx==-1 ? clazz.getName() : className.substring(lastDotIdx+1); 040 URL classResource = clazz.getResource(shortName+CLASS_EXTENSION); 041 if (classResource!=null) { 042 if ("jar".equalsIgnoreCase(classResource.getProtocol())) { 043 String path = classResource.getPath(); 044 int exlaIdx = path.indexOf('!'); 045 if (exlaIdx!=-1) { 046 path = path.substring(0, exlaIdx); 047 try { 048 return new URL(path); 049 } catch (MalformedURLException e) { 050 logger.warning(e.toString()); 051 } 052 } 053 } else { 054 String postfix = clazz.getName().replace('.', '/')+CLASS_EXTENSION; 055 String cref = classResource.toExternalForm(); 056 if (cref.endsWith(postfix)) { 057 try { 058 return new URL(cref.substring(0, cref.length()-postfix.length()-1)); 059 } catch (MalformedURLException e) { 060 logger.warning(e.toString()); 061 } 062 } 063 } 064 } 065 return null; 066 } 067 068 public static List<URL> scan(Class<?> clazz) { 069 List<URL> ret = new ArrayList<URL>(); 070 if (clazz.getClassLoader()!=null) { 071 URL cc = classContainer(clazz); 072 if (cc!=null) { 073 ret.add(cc); 074 URL rtJar = classContainer(Object.class); 075 String rtJarPath = rtJar.getPath(); 076 int idx = rtJarPath.lastIndexOf('/'); 077 scanEntry(ret, clazz.getClassLoader(), 0, idx == -1 ? rtJarPath : rtJarPath.substring(0, idx)); 078 } 079 } 080 return ret; 081 } 082 083 private static void scanEntry(List<URL> collector, ClassLoader classLoader, int idx, String jreLibDir) { 084 URL toScan = collector.get(idx); 085 String toScanPath = toScan.getPath(); 086 int slashIdx = toScanPath.lastIndexOf('/'); 087 if (slashIdx!=-1 && toScanPath.substring(0, slashIdx).equals(jreLibDir)) { 088 scanEntry(collector, classLoader, idx+1, jreLibDir); 089 return; 090 } 091 092 ClassLoaderRepository repo = repoCache.get(classLoader); 093 if (repo == null) { 094 repo = new ClassLoaderRepository(classLoader); 095 repoCache.put(classLoader, repo); 096 } 097 098 try { 099 if ("file".equalsIgnoreCase(toScan.getProtocol()) && new File(toScan.toURI()).isDirectory()) { 100 for (File child: new File(toScan.toURI()).listFiles()) { 101 scanFile(collector, child, null, repo, classLoader, jreLibDir); 102 } 103 } else { 104 ZipInputStream zis = new ZipInputStream(toScan.openStream()); 105 ZipEntry ze; 106 while ((ze = zis.getNextEntry())!=null) { 107 if ("META-INF/manifest.mf".equalsIgnoreCase(ze.getName())) { 108 Manifest manifest = new Manifest(zis); 109 Attributes mattrs = manifest.getMainAttributes(); 110 String classPath = mattrs.getValue("Class-Path"); 111 if (classPath!=null) { 112 for (String cpe: classPath.split(" ")) { 113 try { 114 URL reference = new URL(toScan, cpe); 115 if (!collector.contains(reference)) { 116 collector.add(reference); 117 } 118 } catch (MalformedURLException mee) { 119 logger.warning(mee.toString()); 120 } 121 } 122 } 123 } else if (ze.getName().endsWith(CLASS_EXTENSION)) { 124 JavaClass jc = repo.loadClass(ze.getName().substring(0, ze.getName().length() - CLASS_EXTENSION.length()).replace('/', '.')); 125 ConstantPool constantPool = jc.getConstantPool(); 126 for (Constant constant: constantPool.getConstantPool()) { 127 if (constant instanceof ConstantClass) { 128 ConstantClass cc = (ConstantClass) constant; 129 String cName = String.valueOf(cc.getConstantValue(constantPool)).replace('/', '.'); 130 if (cName.startsWith("[")) { 131 continue; 132 } 133 URL container = classContainer(classLoader.loadClass(cName)); 134 if (!collector.contains(container)) { 135 String containerPath = container.getPath(); 136 int sIdx = containerPath.lastIndexOf('/'); 137 if (sIdx!=-1 && containerPath.substring(0, sIdx).equals(jreLibDir)) { 138 continue; 139 } 140 collector.add(container); 141 } 142 } 143 } 144 145 } 146 } 147 } 148 } catch (Exception e) { 149 logger.warning(e.toString()); 150 } 151 152 } 153 154 private static void scanFile(List<URL> collector, File file, String path, ClassLoaderRepository repo, ClassLoader classLoader, String jreLibDir) throws Exception { 155 String fullName = path==null ? file.getName() : path+"/"+file.getName(); 156 if (file.isDirectory()) { 157 for (File child: file.listFiles()) { 158 scanFile(collector, child, fullName, repo, classLoader, jreLibDir); 159 } 160 } else if ("META-INF/manifest.mf".equalsIgnoreCase(fullName)) { 161 Manifest manifest = new Manifest(new FileInputStream(file)); 162 Attributes mattrs = manifest.getMainAttributes(); 163 String classPath = mattrs.getValue("Class-Path"); 164 if (classPath!=null) { 165 for (String cpe: classPath.split(" ")) { 166 try { 167 URL reference = new URL(file.toURI().toURL(), cpe); 168 if (!collector.contains(reference)) { 169 collector.add(reference); 170 } 171 } catch (MalformedURLException mee) { 172 logger.warning(mee.toString()); 173 } 174 } 175 } 176 } else if (file.getName().endsWith(CLASS_EXTENSION)) { 177 JavaClass jc = repo.loadClass(fullName.substring(0, fullName.length() - CLASS_EXTENSION.length()).replace('/', '.')); 178 ConstantPool constantPool = jc.getConstantPool(); 179 for (Constant constant: constantPool.getConstantPool()) { 180 if (constant instanceof ConstantClass) { 181 ConstantClass cc = (ConstantClass) constant; 182 String cName = String.valueOf(cc.getConstantValue(constantPool)).replace('/', '.'); 183 if (cName.startsWith("[")) { 184 continue; 185 } 186 URL container = classContainer(classLoader.loadClass(cName)); 187 if (!collector.contains(container)) { 188 String containerPath = container.getPath(); 189 int sIdx = containerPath.lastIndexOf('/'); 190 if (sIdx!=-1 && containerPath.substring(0, sIdx).equals(jreLibDir)) { 191 continue; 192 } 193 collector.add(container); 194 } 195 } 196 } 197 198 } 199 } 200 201 public static void main(String[] args) throws Exception { 202 203 List<URL> classPath = scan(DependencyScanner.class); 204 for (URL entry: classPath) { 205 System.out.println(entry); 206 } 207 208 } 209 210}