You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ClassPath.java 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585
  1. /* ====================================================================
  2. * The Apache Software License, Version 1.1
  3. *
  4. * Copyright (c) 2001, 2017 The Apache Software Foundation. All rights
  5. * reserved.
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions
  9. * are met:
  10. *
  11. * 1. Redistributions of source code must retain the above copyright
  12. * notice, this list of conditions and the following disclaimer.
  13. *
  14. * 2. Redistributions in binary form must reproduce the above copyright
  15. * notice, this list of conditions and the following disclaimer in
  16. * the documentation and/or other materials provided with the
  17. * distribution.
  18. *
  19. * 3. The end-user documentation included with the redistribution,
  20. * if any, must include the following acknowledgment:
  21. * "This product includes software developed by the
  22. * Apache Software Foundation (http://www.apache.org/)."
  23. * Alternately, this acknowledgment may appear in the software itself,
  24. * if and wherever such third-party acknowledgments normally appear.
  25. *
  26. * 4. The names "Apache" and "Apache Software Foundation" and
  27. * "Apache BCEL" must not be used to endorse or promote products
  28. * derived from this software without prior written permission. For
  29. * written permission, please contact apache@apache.org.
  30. *
  31. * 5. Products derived from this software may not be called "Apache",
  32. * "Apache BCEL", nor may "Apache" appear in their name, without
  33. * prior written permission of the Apache Software Foundation.
  34. *
  35. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  36. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  37. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  38. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  39. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  40. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  41. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  42. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  43. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  44. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  45. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  46. * SUCH DAMAGE.
  47. * ====================================================================
  48. *
  49. * This software consists of voluntary contributions made by many
  50. * individuals on behalf of the Apache Software Foundation. For more
  51. * information on the Apache Software Foundation, please see
  52. * <http://www.apache.org/>.
  53. */
  54. package org.aspectj.apache.bcel.util;
  55. import java.io.ByteArrayInputStream;
  56. import java.io.DataInputStream;
  57. import java.io.File;
  58. import java.io.FileInputStream;
  59. import java.io.FilenameFilter;
  60. import java.io.IOException;
  61. import java.io.InputStream;
  62. import java.io.Serializable;
  63. import java.net.URI;
  64. import java.nio.file.FileSystems;
  65. import java.nio.file.FileVisitResult;
  66. import java.nio.file.Files;
  67. import java.nio.file.Path;
  68. import java.nio.file.SimpleFileVisitor;
  69. import java.nio.file.attribute.BasicFileAttributeView;
  70. import java.nio.file.attribute.BasicFileAttributes;
  71. import java.util.ArrayList;
  72. import java.util.HashMap;
  73. import java.util.Iterator;
  74. import java.util.List;
  75. import java.util.Map;
  76. import java.util.StringTokenizer;
  77. import java.util.zip.ZipEntry;
  78. import java.util.zip.ZipFile;
  79. /**
  80. * Responsible for loading (class) files from the CLASSPATH. Inspired by
  81. * sun.tools.ClassPath.
  82. *
  83. * @author M. Dahm
  84. * @author Mario Ivankovits
  85. * @author Andy Clement
  86. */
  87. public class ClassPath implements Serializable {
  88. private static final String JRT_FS = "jrt-fs.jar";
  89. private static ClassPath SYSTEM_CLASS_PATH = null;
  90. private PathEntry[] paths;
  91. private String class_path;
  92. public static ClassPath getSystemClassPath() {
  93. if (SYSTEM_CLASS_PATH == null) {
  94. SYSTEM_CLASS_PATH = new ClassPath();
  95. }
  96. return SYSTEM_CLASS_PATH;
  97. }
  98. /**
  99. * Search for classes in given path.
  100. */
  101. public ClassPath(String class_path) {
  102. this.class_path = class_path;
  103. List<PathEntry> vec = new ArrayList<>();
  104. for (StringTokenizer tok = new StringTokenizer(class_path, System.getProperty("path.separator")); tok
  105. .hasMoreTokens();) {
  106. String path = tok.nextToken();
  107. if (!path.equals("")) {
  108. File file = new File(path);
  109. try {
  110. if (file.exists()) {
  111. if (file.isDirectory()) {
  112. vec.add(new Dir(path));
  113. } else if (file.getName().endsWith("jrt-fs.jar")) { // TODO a bit crude...
  114. vec.add(new JImage());
  115. } else {
  116. vec.add(new Zip(new ZipFile(file)));
  117. }
  118. }
  119. } catch (IOException e) {
  120. System.err.println("CLASSPATH component " + file + ": " + e);
  121. }
  122. }
  123. }
  124. paths = new PathEntry[vec.size()];
  125. vec.toArray(paths);
  126. }
  127. /**
  128. * Search for classes in CLASSPATH.
  129. *
  130. * @deprecated Use SYSTEM_CLASS_PATH constant
  131. */
  132. @Deprecated
  133. public ClassPath() {
  134. this(getClassPath());
  135. }
  136. /**
  137. * @return used class path string
  138. */
  139. @Override
  140. public String toString() {
  141. return class_path;
  142. }
  143. @Override
  144. public int hashCode() {
  145. return class_path.hashCode();
  146. }
  147. @Override
  148. public boolean equals(Object o) {
  149. if (o instanceof ClassPath) {
  150. return class_path.equals(((ClassPath) o).class_path);
  151. }
  152. return false;
  153. }
  154. private static final void getPathComponents(String path, List<String> list) {
  155. if (path != null) {
  156. StringTokenizer tok = new StringTokenizer(path, File.pathSeparator);
  157. while (tok.hasMoreTokens()) {
  158. String name = tok.nextToken();
  159. File file = new File(name);
  160. if (file.exists())
  161. list.add(name);
  162. }
  163. }
  164. }
  165. /**
  166. * Checks for class path components in the following properties:
  167. * "java.class.path", "sun.boot.class.path", "java.ext.dirs"
  168. *
  169. * @return class path as used by default by BCEL
  170. */
  171. public static final String getClassPath() {
  172. String class_path = System.getProperty("java.class.path");
  173. String boot_path = System.getProperty("sun.boot.class.path");
  174. String ext_path = System.getProperty("java.ext.dirs");
  175. String vm_version = System.getProperty("java.version");
  176. ArrayList<String> list = new ArrayList<>();
  177. getPathComponents(class_path, list);
  178. getPathComponents(boot_path, list);
  179. ArrayList<String> dirs = new ArrayList<>();
  180. getPathComponents(ext_path, dirs);
  181. for (String string : dirs) {
  182. File ext_dir = new File(string);
  183. String[] extensions = ext_dir.list(new FilenameFilter() {
  184. @Override
  185. public boolean accept(File dir, String name) {
  186. name = name.toLowerCase();
  187. return name.endsWith(".zip") || name.endsWith(".jar");
  188. }
  189. });
  190. if (extensions != null)
  191. for (String extension : extensions)
  192. list.add(ext_dir.toString() + File.separatorChar + extension);
  193. }
  194. StringBuilder buf = new StringBuilder();
  195. for (Iterator<String> e = list.iterator(); e.hasNext();) {
  196. buf.append(e.next());
  197. if (e.hasNext())
  198. buf.append(File.pathSeparatorChar);
  199. }
  200. // On Java9 the sun.boot.class.path won't be set. System classes accessible through JRT filesystem
  201. if (vm_version.matches("^(9|10|11|12|13|14|15|16|17|18|19).*")) {
  202. buf.insert(0, File.pathSeparatorChar);
  203. buf.insert(0, System.getProperty("java.home") + File.separator + "lib" + File.separator + JRT_FS);
  204. }
  205. return buf.toString().intern();
  206. }
  207. /**
  208. * @param name
  209. * fully qualified class name, e.g. java.lang.String
  210. * @return input stream for class
  211. */
  212. public InputStream getInputStream(String name) throws IOException {
  213. return getInputStream(name, ".class");
  214. }
  215. /**
  216. * Return stream for class or resource on CLASSPATH.
  217. *
  218. * @param name
  219. * fully qualified file name, e.g. java/lang/String
  220. * @param suffix
  221. * file name ends with suff, e.g. .java
  222. * @return input stream for file on class path
  223. */
  224. public InputStream getInputStream(String name, String suffix) throws IOException {
  225. InputStream is = null;
  226. try {
  227. is = getClass().getClassLoader().getResourceAsStream(name + suffix);
  228. } catch (Exception e) {
  229. }
  230. if (is != null)
  231. return is;
  232. return getClassFile(name, suffix).getInputStream();
  233. }
  234. /**
  235. * @param name
  236. * fully qualified file name, e.g. java/lang/String
  237. * @param suffix
  238. * file name ends with suff, e.g. .java
  239. * @return class file for the java class
  240. */
  241. public ClassFile getClassFile(String name, String suffix) throws IOException {
  242. for (PathEntry path : paths) {
  243. ClassFile cf;
  244. if ((cf = path.getClassFile(name, suffix)) != null)
  245. return cf;
  246. }
  247. throw new IOException("Couldn't find: " + name + suffix);
  248. }
  249. /**
  250. * @param name
  251. * fully qualified class name, e.g. java.lang.String
  252. * @return input stream for class
  253. */
  254. public ClassFile getClassFile(String name) throws IOException {
  255. return getClassFile(name, ".class");
  256. }
  257. /**
  258. * @param name
  259. * fully qualified file name, e.g. java/lang/String
  260. * @param suffix
  261. * file name ends with suffix, e.g. .java
  262. * @return byte array for file on class path
  263. */
  264. public byte[] getBytes(String name, String suffix) throws IOException {
  265. InputStream is = getInputStream(name, suffix);
  266. if (is == null)
  267. throw new IOException("Couldn't find: " + name + suffix);
  268. DataInputStream dis = new DataInputStream(is);
  269. byte[] bytes = new byte[is.available()];
  270. dis.readFully(bytes);
  271. dis.close();
  272. is.close();
  273. return bytes;
  274. }
  275. /**
  276. * @return byte array for class
  277. */
  278. public byte[] getBytes(String name) throws IOException {
  279. return getBytes(name, ".class");
  280. }
  281. /**
  282. * @param name
  283. * name of file to search for, e.g. java/lang/String.java
  284. * @return full (canonical) path for file
  285. */
  286. public String getPath(String name) throws IOException {
  287. int index = name.lastIndexOf('.');
  288. String suffix = "";
  289. if (index > 0) {
  290. suffix = name.substring(index);
  291. name = name.substring(0, index);
  292. }
  293. return getPath(name, suffix);
  294. }
  295. /**
  296. * @param name
  297. * name of file to search for, e.g. java/lang/String
  298. * @param suffix
  299. * file name suffix, e.g. .java
  300. * @return full (canonical) path for file, if it exists
  301. */
  302. public String getPath(String name, String suffix) throws IOException {
  303. return getClassFile(name, suffix).getPath();
  304. }
  305. private static abstract class PathEntry implements Serializable {
  306. abstract ClassFile getClassFile(String name, String suffix) throws IOException;
  307. }
  308. /**
  309. * Contains information about file/ZIP entry of the Java class.
  310. */
  311. public interface ClassFile {
  312. /**
  313. * @return input stream for class file.
  314. */
  315. InputStream getInputStream() throws IOException;
  316. /**
  317. * @return canonical path to class file.
  318. */
  319. String getPath();
  320. /**
  321. * @return base path of found class, i.e. class is contained relative to
  322. * that path, which may either denote a directory, or zip file
  323. */
  324. String getBase();
  325. /**
  326. * @return modification time of class file.
  327. */
  328. long getTime();
  329. /**
  330. * @return size of class file.
  331. */
  332. long getSize();
  333. }
  334. private static class Dir extends PathEntry {
  335. private String dir;
  336. Dir(String d) {
  337. dir = d;
  338. }
  339. @Override
  340. ClassFile getClassFile(String name, String suffix) throws IOException {
  341. final File file = new File(dir + File.separatorChar + name.replace('.', File.separatorChar) + suffix);
  342. return file.exists() ? new ClassFile() {
  343. @Override
  344. public InputStream getInputStream() throws IOException {
  345. return new FileInputStream(file);
  346. }
  347. @Override
  348. public String getPath() {
  349. try {
  350. return file.getCanonicalPath();
  351. } catch (IOException e) {
  352. return null;
  353. }
  354. }
  355. @Override
  356. public long getTime() {
  357. return file.lastModified();
  358. }
  359. @Override
  360. public long getSize() {
  361. return file.length();
  362. }
  363. @Override
  364. public String getBase() {
  365. return dir;
  366. }
  367. } : null;
  368. }
  369. @Override
  370. public String toString() {
  371. return dir;
  372. }
  373. }
  374. private static class JImage extends PathEntry {
  375. private static URI JRT_URI = URI.create("jrt:/"); //$NON-NLS-1$
  376. private static String MODULES_PATH = "modules"; //$NON-NLS-1$
  377. private static String JAVA_BASE_PATH = "java.base"; //$NON-NLS-1$
  378. private java.nio.file.FileSystem fs;
  379. private final Map<String, Path> fileMap;
  380. JImage() {
  381. fs = FileSystems.getFileSystem(JRT_URI);
  382. fileMap = buildFileMap();
  383. }
  384. private Map<String, Path> buildFileMap() {
  385. final Map<String, Path> fileMap = new HashMap<>();
  386. final java.nio.file.PathMatcher matcher = fs.getPathMatcher("glob:*.class");
  387. Iterable<java.nio.file.Path> roots = fs.getRootDirectories();
  388. for (java.nio.file.Path path : roots) {
  389. try {
  390. Files.walkFileTree(path, new SimpleFileVisitor<Path>() {
  391. @Override
  392. public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
  393. if (file.getNameCount() > 2
  394. && matcher.matches(file.getFileName())) {
  395. Path classPath = file.subpath(2, file.getNameCount());
  396. fileMap.put(classPath.toString(), file);
  397. }
  398. return FileVisitResult.CONTINUE;
  399. }
  400. });
  401. }
  402. catch (IOException e) {
  403. throw new RuntimeException(e);
  404. }
  405. }
  406. return fileMap;
  407. }
  408. private static class ByteBasedClassFile implements ClassFile {
  409. private byte[] bytes;
  410. private ByteArrayInputStream bais;
  411. private String path;
  412. private String base;
  413. private long time;
  414. private long size;
  415. public ByteBasedClassFile(byte[] bytes, String path, String base, long time, long size) {
  416. this.bytes = bytes;
  417. this.path = path;
  418. this.base = base;
  419. this.time = time;
  420. this.size = size;
  421. }
  422. @Override
  423. public InputStream getInputStream() throws IOException {
  424. // TODO too costly to keep these in inflated form in memory?
  425. this.bais = new ByteArrayInputStream(bytes);
  426. return this.bais;
  427. }
  428. @Override
  429. public String getPath() {
  430. return this.path;
  431. }
  432. @Override
  433. public String getBase() {
  434. return this.base;
  435. }
  436. @Override
  437. public long getTime() {
  438. return this.time;
  439. }
  440. @Override
  441. public long getSize() {
  442. return this.size;
  443. }
  444. }
  445. @Override
  446. ClassFile getClassFile(String name, String suffix) throws IOException {
  447. // Class files are in here under names like this:
  448. // /modules/java.base/java/lang/Object.class (jdk9 b74)
  449. // so within a modules top level qualifier and then the java.base module
  450. String fileName = name.replace('.', '/') + suffix;
  451. Path p = fileMap.get(fileName);
  452. if (p == null) {
  453. return null;
  454. }
  455. byte[] bs = Files.readAllBytes(p);
  456. BasicFileAttributeView bfav = Files.getFileAttributeView(p, BasicFileAttributeView.class);
  457. BasicFileAttributes bfas = bfav.readAttributes();
  458. long time = bfas.lastModifiedTime().toMillis();
  459. long size = bfas.size();
  460. ClassFile cf = new ByteBasedClassFile(bs, "jimage",fileName,time,size);
  461. return cf;
  462. }
  463. }
  464. private static class Zip extends PathEntry {
  465. private ZipFile zip;
  466. Zip(ZipFile z) {
  467. zip = z;
  468. }
  469. @Override
  470. ClassFile getClassFile(String name, String suffix) throws IOException {
  471. final ZipEntry entry = zip.getEntry(name.replace('.', '/') + suffix);
  472. return (entry != null) ? new ClassFile() {
  473. @Override
  474. public InputStream getInputStream() throws IOException {
  475. return zip.getInputStream(entry);
  476. }
  477. @Override
  478. public String getPath() {
  479. return entry.toString();
  480. }
  481. @Override
  482. public long getTime() {
  483. return entry.getTime();
  484. }
  485. @Override
  486. public long getSize() {
  487. return entry.getSize();
  488. }
  489. @Override
  490. public String getBase() {
  491. return zip.getName();
  492. }
  493. } : null;
  494. }
  495. }
  496. }