1 /* 2 * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.tools.jdeprscan.scan; 27 28 import com.sun.tools.classfile.ClassFile; 29 import com.sun.tools.classfile.ConstantPoolException; 30 31 import java.io.IOException; 32 import java.net.URI; 33 import java.nio.file.FileSystem; 34 import java.nio.file.FileSystems; 35 import java.nio.file.Files; 36 import java.nio.file.NoSuchFileException; 37 import java.nio.file.Path; 38 import java.nio.file.Paths; 39 import java.util.ArrayList; 40 import java.util.List; 41 import java.util.Optional; 42 import java.util.jar.JarEntry; 43 import java.util.jar.JarFile; 44 import java.util.stream.Stream; 45 46 /** 47 * A simple search path for classes. 48 */ 49 public class ClassFinder { 50 final List<PathEntry> list = new ArrayList<>(); 51 final boolean verbose; 52 ClassFinder(boolean verbose)53 public ClassFinder(boolean verbose) { 54 this.verbose = verbose; 55 } 56 57 /** 58 * Adds a directory to this finder's search path, ignoring errors. 59 * 60 * @param dirName the directory to add 61 */ addDir(String dirName)62 public void addDir(String dirName) { 63 Path dir = Paths.get(dirName); 64 65 if (Files.isDirectory(dir)) { 66 list.add(new DirPathEntry(dir)); 67 } 68 } 69 70 /** 71 * Adds a jar file to this finder's search path, ignoring errors. 72 * 73 * @param jarName the jar file name to add 74 */ addJar(String jarName)75 public void addJar(String jarName) { 76 try { 77 list.add(new JarPathEntry(new JarFile(jarName))); 78 } catch (IOException ignore) { } 79 } 80 81 /** 82 * Adds the JRT filesystem to this finder's search path. 83 */ addJrt()84 public void addJrt() { 85 list.add(new JrtPathEntry()); 86 } 87 88 /** 89 * Searches the class path for a class with the given name, 90 * returning a ClassFile for it. Returns null if not found. 91 * 92 * @param className the class to search for 93 * @return a ClassFile instance, or null if not found 94 */ find(String className)95 public ClassFile find(String className) { 96 for (PathEntry pe : list) { 97 ClassFile cf = pe.find(className); 98 if (cf != null) { 99 return cf; 100 } 101 } 102 return null; 103 } 104 105 /** 106 * An entry in this finder's class path. 107 */ 108 interface PathEntry { 109 /** 110 * Returns a ClassFile instance corresponding to this name, 111 * or null if it's not present in this entry. 112 * 113 * @param className the class to search for 114 * @return a ClassFile instance, or null if not found 115 */ find(String className)116 ClassFile find(String className); 117 } 118 119 /** 120 * An entry that represents a jar file. 121 */ 122 class JarPathEntry implements PathEntry { 123 final JarFile jarFile; 124 JarPathEntry(JarFile jf)125 JarPathEntry(JarFile jf) { 126 jarFile = jf; 127 } 128 129 @Override find(String className)130 public ClassFile find(String className) { 131 JarEntry entry = jarFile.getJarEntry(className + ".class"); 132 if (entry == null) { 133 return null; 134 } 135 try { 136 return ClassFile.read(jarFile.getInputStream(entry)); 137 } catch (IOException | ConstantPoolException ex) { 138 if (verbose) { 139 ex.printStackTrace(); 140 } 141 } 142 return null; 143 } 144 } 145 146 /** 147 * An entry that represents a directory containing a class hierarchy. 148 */ 149 class DirPathEntry implements PathEntry { 150 final Path dir; 151 DirPathEntry(Path dir)152 DirPathEntry(Path dir) { 153 this.dir = dir; 154 } 155 156 @Override find(String className)157 public ClassFile find(String className) { 158 Path classFileName = dir.resolve(className + ".class"); 159 try { 160 return ClassFile.read(classFileName); 161 } catch (NoSuchFileException nsfe) { 162 // not found, return silently 163 } catch (IOException | ConstantPoolException ex) { 164 if (verbose) { 165 ex.printStackTrace(); 166 } 167 } 168 return null; 169 } 170 } 171 172 /** 173 * An entry that represents the JRT filesystem in the running image. 174 * 175 * JRT filesystem structure is: 176 * /packages/<dotted-pkgname>/<modlink> 177 * where modlink is a symbolic link to /modules/<modname> which is 178 * the top of the usual package-class hierarchy 179 */ 180 class JrtPathEntry implements PathEntry { 181 final FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/")); 182 183 @Override find(String className)184 public ClassFile find(String className) { 185 int end = className.lastIndexOf('/'); 186 if (end < 0) { 187 return null; 188 } 189 String pkg = "/packages/" + className.substring(0, end) 190 .replace('/', '.'); 191 try (Stream<Path> mods = Files.list(fs.getPath(pkg))) { 192 Optional<Path> opath = 193 mods.map(path -> path.resolve(className + ".class")) 194 .filter(Files::exists) 195 .findFirst(); 196 if (opath.isPresent()) { 197 return ClassFile.read(opath.get()); 198 } else { 199 return null; 200 } 201 } catch (NoSuchFileException nsfe) { 202 // not found, return silently 203 } catch (IOException | ConstantPoolException ex) { 204 if (verbose) { 205 ex.printStackTrace(); 206 } 207 } 208 return null; 209 } 210 } 211 } 212