Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

ClassPathManager.java 16KB

vor 21 Jahren
vor 21 Jahren
vor 21 Jahren
vor 21 Jahren
vor 21 Jahren
vor 21 Jahren
vor 8 Jahren
vor 12 Jahren
vor 8 Jahren
vor 8 Jahren
vor 8 Jahren
vor 8 Jahren
vor 12 Jahren
vor 8 Jahren
vor 8 Jahren
vor 8 Jahren
vor 8 Jahren
vor 21 Jahren
vor 8 Jahren
vor 21 Jahren
vor 8 Jahren
vor 21 Jahren
vor 12 Jahren
vor 8 Jahren
vor 8 Jahren
vor 8 Jahren
vor 8 Jahren
vor 21 Jahren
vor 21 Jahren
vor 21 Jahren
vor 21 Jahren
vor 21 Jahren
vor 21 Jahren
vor 21 Jahren
vor 21 Jahren
vor 21 Jahren
vor 21 Jahren
vor 21 Jahren
vor 21 Jahren
vor 21 Jahren
vor 21 Jahren
vor 21 Jahren
vor 21 Jahren
vor 21 Jahren
vor 21 Jahren
vor 21 Jahren
vor 21 Jahren
vor 21 Jahren
vor 21 Jahren
vor 21 Jahren
vor 21 Jahren
vor 21 Jahren
vor 21 Jahren
vor 21 Jahren
vor 12 Jahren
vor 12 Jahren
vor 21 Jahren
vor 21 Jahren
vor 21 Jahren
vor 8 Jahren
vor 8 Jahren
vor 21 Jahren
vor 21 Jahren
vor 21 Jahren
vor 8 Jahren
vor 21 Jahren
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615
  1. /* *******************************************************************
  2. * Copyright (c) 2002, 2017 Contributors
  3. * All rights reserved.
  4. * This program and the accompanying materials are made available
  5. * under the terms of the Eclipse Public License v 2.0
  6. * which accompanies this distribution and is available at
  7. * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt
  8. *
  9. * Contributors:
  10. * Palo Alto Research Center, Incorporated (PARC).
  11. * ******************************************************************/
  12. package org.aspectj.weaver.bcel;
  13. import java.io.ByteArrayInputStream;
  14. import java.io.File;
  15. import java.io.FileInputStream;
  16. import java.io.FileNotFoundException;
  17. import java.io.IOException;
  18. import java.io.InputStream;
  19. import java.net.MalformedURLException;
  20. import java.net.URI;
  21. import java.net.URL;
  22. import java.net.URLClassLoader;
  23. import java.nio.file.FileSystem;
  24. import java.nio.file.FileSystems;
  25. import java.nio.file.FileVisitResult;
  26. import java.nio.file.Files;
  27. import java.nio.file.Path;
  28. import java.nio.file.SimpleFileVisitor;
  29. import java.nio.file.attribute.BasicFileAttributes;
  30. import java.util.ArrayList;
  31. import java.util.Enumeration;
  32. import java.util.HashMap;
  33. import java.util.Iterator;
  34. import java.util.List;
  35. import java.util.Map;
  36. import java.util.zip.ZipEntry;
  37. import java.util.zip.ZipFile;
  38. import org.aspectj.bridge.IMessageHandler;
  39. import org.aspectj.bridge.MessageUtil;
  40. import org.aspectj.util.LangUtil;
  41. import org.aspectj.util.SoftHashMap;
  42. import org.aspectj.weaver.BCException;
  43. import org.aspectj.weaver.UnresolvedType;
  44. import org.aspectj.weaver.WeaverMessages;
  45. import org.aspectj.weaver.tools.Trace;
  46. import org.aspectj.weaver.tools.TraceFactory;
  47. /**
  48. * @author Andy Clement
  49. * @author Mario Ivankovits
  50. */
  51. public class ClassPathManager {
  52. private static Trace trace = TraceFactory.getTraceFactory().getTrace(ClassPathManager.class);
  53. private static int maxOpenArchives = -1;
  54. private static URI JRT_URI = URI.create("jrt:/"); //$NON-NLS-1$
  55. private static final int MAXOPEN_DEFAULT = 1000;
  56. private List<Entry> entries;
  57. // In order to control how many open files we have, we maintain a list.
  58. // The max number is configured through the property:
  59. // org.aspectj.weaver.openarchives
  60. // and it defaults to 1000
  61. private List<ZipFile> openArchives = new ArrayList<>();
  62. static {
  63. String openzipsString = getSystemPropertyWithoutSecurityException("org.aspectj.weaver.openarchives",
  64. Integer.toString(MAXOPEN_DEFAULT));
  65. maxOpenArchives = Integer.parseInt(openzipsString);
  66. if (maxOpenArchives < 20) {
  67. maxOpenArchives = 1000;
  68. }
  69. }
  70. public ClassPathManager(List<String> classpath, IMessageHandler handler) {
  71. if (trace.isTraceEnabled()) {
  72. trace.enter("<init>", this, new Object[] { classpath==null?"null":classpath.toString(), handler });
  73. }
  74. entries = new ArrayList<>();
  75. for (String classpathEntry: classpath) {
  76. addPath(classpathEntry,handler);
  77. }
  78. if (trace.isTraceEnabled()) {
  79. trace.exit("<init>");
  80. }
  81. }
  82. protected ClassPathManager() {
  83. }
  84. public void addPath(String name, IMessageHandler handler) {
  85. File f = new File(name);
  86. if (!f.isDirectory()) {
  87. if (!f.isFile()) {
  88. if (!name.toLowerCase().endsWith(".jar") || name.toLowerCase().endsWith(".zip")) {
  89. // heuristic-only: ending with .jar or .zip means probably a zip file
  90. MessageUtil.info(handler, WeaverMessages.format(WeaverMessages.ZIPFILE_ENTRY_MISSING, name));
  91. } else {
  92. MessageUtil.info(handler, WeaverMessages.format(WeaverMessages.DIRECTORY_ENTRY_MISSING, name));
  93. }
  94. return;
  95. }
  96. try {
  97. if (name.toLowerCase().endsWith(LangUtil.JRT_FS)) { // Java9+
  98. entries.add(new JImageEntry(name));
  99. } else {
  100. entries.add(new ZipFileEntry(f));
  101. }
  102. } catch (IOException ioe) {
  103. MessageUtil.warn(handler,
  104. WeaverMessages.format(WeaverMessages.ZIPFILE_ENTRY_INVALID, name, ioe.getMessage()));
  105. return;
  106. }
  107. } else {
  108. entries.add(new DirEntry(f));
  109. }
  110. }
  111. public ClassFile find(UnresolvedType type) {
  112. if (trace.isTraceEnabled()) {
  113. trace.enter("find", this, type);
  114. }
  115. String name = type.getName();
  116. for (Iterator<Entry> i = entries.iterator(); i.hasNext();) {
  117. Entry entry = i.next();
  118. try {
  119. ClassFile ret = entry.find(name);
  120. if (trace.isTraceEnabled()) {
  121. trace.event("searching for "+type+" in "+entry.toString());
  122. }
  123. if (ret != null) {
  124. if (trace.isTraceEnabled()) {
  125. trace.exit("find", ret);
  126. }
  127. return ret;
  128. }
  129. } catch (IOException ioe) {
  130. // this is NOT an error: it's valid to have missing classpath entries
  131. if (trace.isTraceEnabled()) {
  132. trace.error("Removing classpath entry for "+entry,ioe);
  133. }
  134. i.remove();
  135. }
  136. }
  137. if (trace.isTraceEnabled()) {
  138. trace.exit("find", null);
  139. }
  140. return null;
  141. }
  142. @Override
  143. public String toString() {
  144. StringBuilder buf = new StringBuilder();
  145. boolean start = true;
  146. for (Entry entry : entries) {
  147. if (start) {
  148. start = false;
  149. } else {
  150. buf.append(File.pathSeparator);
  151. }
  152. buf.append(entry);
  153. }
  154. return buf.toString();
  155. }
  156. public abstract static class ClassFile {
  157. public abstract InputStream getInputStream() throws IOException;
  158. public abstract String getPath();
  159. public abstract void close();
  160. }
  161. abstract static class Entry {
  162. public abstract ClassFile find(String name) throws IOException;
  163. }
  164. static class ByteBasedClassFile extends ClassFile {
  165. private byte[] bytes;
  166. private ByteArrayInputStream bais;
  167. private String path;
  168. public ByteBasedClassFile(byte[] bytes, String path) {
  169. this.bytes = bytes;
  170. this.path = path;
  171. }
  172. @Override
  173. public InputStream getInputStream() throws IOException {
  174. this.bais = new ByteArrayInputStream(bytes);
  175. return this.bais;
  176. }
  177. @Override
  178. public String getPath() {
  179. return this.path;
  180. }
  181. @Override
  182. public void close() {
  183. if (this.bais!=null) {
  184. try {
  185. this.bais.close();
  186. } catch (IOException e) {
  187. }
  188. this.bais = null;
  189. }
  190. }
  191. }
  192. static class FileClassFile extends ClassFile {
  193. private File file;
  194. private FileInputStream fis;
  195. public FileClassFile(File file) {
  196. this.file = file;
  197. }
  198. @Override
  199. public InputStream getInputStream() throws IOException {
  200. fis = new FileInputStream(file);
  201. return fis;
  202. }
  203. @Override
  204. public void close() {
  205. try {
  206. if (fis != null)
  207. fis.close();
  208. } catch (IOException ioe) {
  209. throw new BCException("Can't close class file : " + file.getName(), ioe);
  210. } finally {
  211. fis = null;
  212. }
  213. }
  214. @Override
  215. public String getPath() {
  216. return file.getPath();
  217. }
  218. }
  219. class DirEntry extends Entry {
  220. private String dirPath;
  221. public DirEntry(File dir) {
  222. this.dirPath = dir.getPath();
  223. }
  224. public DirEntry(String dirPath) {
  225. this.dirPath = dirPath;
  226. }
  227. @Override
  228. public ClassFile find(String name) {
  229. File f = new File(dirPath + File.separator + name.replace('.', File.separatorChar) + ".class");
  230. if (f.isFile())
  231. return new FileClassFile(f);
  232. else
  233. return null;
  234. }
  235. @Override
  236. public String toString() {
  237. return dirPath;
  238. }
  239. }
  240. static class ZipEntryClassFile extends ClassFile {
  241. private ZipEntry entry;
  242. private ZipFileEntry zipFile;
  243. private InputStream is;
  244. public ZipEntryClassFile(ZipFileEntry zipFile, ZipEntry entry) {
  245. this.zipFile = zipFile;
  246. this.entry = entry;
  247. }
  248. @Override
  249. public InputStream getInputStream() throws IOException {
  250. is = zipFile.getZipFile().getInputStream(entry);
  251. return is;
  252. }
  253. @Override
  254. public void close() {
  255. try {
  256. if (is != null)
  257. is.close();
  258. } catch (IOException e) {
  259. e.printStackTrace();
  260. } finally {
  261. is = null;
  262. }
  263. }
  264. @Override
  265. public String getPath() {
  266. return entry.getName();
  267. }
  268. }
  269. /**
  270. * Maintains a shared package cache for java runtime image. This maps packages (for example:
  271. * java/lang) to a starting root position in the filesystem (for example: /modules/java.base/java/lang).
  272. * When searching for a type we work out the package name, use it to find where in the filesystem
  273. * to start looking then run from there. Once found we do cache what we learn to make subsequent
  274. * lookups of that type even faster. Maintaining just a package cache rather than complete type cache
  275. * helps reduce memory usage but still gives reasonably fast lookup performance.
  276. */
  277. static class JImageEntry extends Entry {
  278. // Map from a JRT-FS file to the cache state for that file
  279. private static Map<String, JImageState> states = new HashMap<>();
  280. private JImageState state;
  281. // TODO memory management here - is it held onto too long when LTW?
  282. static class JImageState {
  283. private final String jrtFsPath;
  284. private final FileSystem fs;
  285. Map<String,Path> fileCache = new SoftHashMap<>();
  286. boolean packageCacheInitialized = false;
  287. Map<String,Path> packageCache = new HashMap<>();
  288. public JImageState(String jrtFsPath, FileSystem fs) {
  289. this.jrtFsPath = jrtFsPath;
  290. this.fs = fs;
  291. }
  292. }
  293. public JImageEntry(String jrtFsPath) {
  294. state = states.get(jrtFsPath);
  295. if (state == null) {
  296. synchronized (states) {
  297. if (state == null) {
  298. URL jrtPath = null;
  299. try {
  300. jrtPath = new File(jrtFsPath).toPath().toUri().toURL();
  301. } catch (MalformedURLException e) {
  302. System.out.println("Unexpected problem processing "+jrtFsPath+" bad classpath entry? skipping:"+e.getMessage());
  303. return;
  304. }
  305. String jdkHome = new File(jrtFsPath).getParentFile().getParent();
  306. FileSystem fs = null;
  307. try {
  308. if (LangUtil.isVMGreaterOrEqual(9)) {
  309. Map<String, String> env = new HashMap<>();
  310. env.put("java.home", jdkHome);
  311. fs = FileSystems.newFileSystem(JRT_URI, env);
  312. } else {
  313. URLClassLoader loader = new URLClassLoader(new URL[] { jrtPath });
  314. Map<String, ?> env = new HashMap<>();
  315. fs = FileSystems.newFileSystem(JRT_URI, env, loader);
  316. }
  317. state = new JImageState(jrtFsPath, fs);
  318. states.put(jrtFsPath, state);
  319. buildPackageMap();
  320. } catch (Throwable t) {
  321. throw new IllegalStateException("Unexpectedly unable to initialize a JRT filesystem", t);
  322. }
  323. }
  324. }
  325. }
  326. }
  327. class PackageCacheBuilderVisitor extends SimpleFileVisitor<Path> {
  328. @Override
  329. public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
  330. if (file.getNameCount() > 3 && file.toString().endsWith(".class")) {
  331. int fnc = file.getNameCount();
  332. if (fnc > 3) { // There is a package name - e.g. /modules/java.base/java/lang/Object.class
  333. Path packagePath = file.subpath(2, fnc-1); // e.g. java/lang
  334. String packagePathString = packagePath.toString();
  335. state.packageCache.put(packagePathString, file.subpath(0, fnc-1)); // java/lang -> /modules/java.base/java/lang
  336. }
  337. }
  338. return FileVisitResult.CONTINUE;
  339. }
  340. }
  341. /**
  342. * Create a map from package names to the specific directory of the package members in the filesystem.
  343. */
  344. private synchronized void buildPackageMap() {
  345. if (!state.packageCacheInitialized) {
  346. state.packageCacheInitialized = true;
  347. Iterable<java.nio.file.Path> roots = state.fs.getRootDirectories();
  348. PackageCacheBuilderVisitor visitor = new PackageCacheBuilderVisitor();
  349. try {
  350. for (java.nio.file.Path path : roots) {
  351. Files.walkFileTree(path, visitor);
  352. }
  353. } catch (IOException e) {
  354. throw new RuntimeException(e);
  355. }
  356. }
  357. }
  358. class TypeIdentifier extends SimpleFileVisitor<Path> {
  359. // What are we looking for?
  360. private String name;
  361. // If set, where did we find it?
  362. public Path found;
  363. // Basic metric count of how many files we checked before finding it
  364. public int filesSearchedCount;
  365. public TypeIdentifier(String name) {
  366. this.name = name;
  367. }
  368. @Override
  369. public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
  370. filesSearchedCount++;
  371. if (file.getNameCount() > 2 && file.toString().endsWith(".class")) {
  372. int fnc = file.getNameCount();
  373. Path filePath = file.subpath(2, fnc);
  374. String filePathString = filePath.toString();
  375. if (filePathString.equals(name)) {
  376. state.fileCache.put(filePathString, file);
  377. found = file;
  378. return FileVisitResult.TERMINATE;
  379. }
  380. }
  381. return FileVisitResult.CONTINUE;
  382. }
  383. }
  384. private Path searchForFileAndCache(final Path startPath, final String name) {
  385. TypeIdentifier locator = new TypeIdentifier(name);
  386. try {
  387. Files.walkFileTree(startPath, locator);
  388. } catch (IOException e) {
  389. throw new RuntimeException(e);
  390. }
  391. return locator.found;
  392. }
  393. @Override
  394. public ClassFile find(String name) throws IOException {
  395. String fileName = name.replace('.', '/') + ".class";
  396. Path file = state.fileCache.get(fileName);
  397. if (file == null) {
  398. // Check the packages map to see if we know about this package
  399. int idx = fileName.lastIndexOf('/');
  400. if (idx == -1) {
  401. // Package not here
  402. return null;
  403. }
  404. Path packageStart = null;
  405. String packageName = null;
  406. if (idx !=-1 ) {
  407. packageName = fileName.substring(0, idx);
  408. packageStart = state.packageCache.get(packageName);
  409. if (packageStart != null) {
  410. file = searchForFileAndCache(packageStart, fileName);
  411. }
  412. }
  413. }
  414. if (file == null) {
  415. return null;
  416. }
  417. byte[] bs = Files.readAllBytes(file);
  418. ClassFile cf = new ByteBasedClassFile(bs, fileName);
  419. return cf;
  420. }
  421. Map<String, Path> getPackageCache() {
  422. return state.packageCache;
  423. }
  424. Map<String, Path> getFileCache() {
  425. return state.fileCache;
  426. }
  427. }
  428. class ZipFileEntry extends Entry {
  429. private File file;
  430. private ZipFile zipFile;
  431. public ZipFileEntry(File file) throws IOException {
  432. this.file = file;
  433. }
  434. public ZipFileEntry(ZipFile zipFile) {
  435. this.zipFile = zipFile;
  436. }
  437. public ZipFile getZipFile() {
  438. return zipFile;
  439. }
  440. @Override
  441. public ClassFile find(String name) throws IOException {
  442. ensureOpen();
  443. String key = name.replace('.', '/') + ".class";
  444. ZipEntry entry = zipFile.getEntry(key);
  445. if (entry != null)
  446. return new ZipEntryClassFile(this, entry);
  447. else
  448. return null; // This zip will be closed when necessary...
  449. }
  450. public List<ZipEntryClassFile> getAllClassFiles() throws IOException {
  451. ensureOpen();
  452. List<ZipEntryClassFile> ret = new ArrayList<>();
  453. for (Enumeration<? extends ZipEntry> e = zipFile.entries(); e.hasMoreElements();) {
  454. ZipEntry entry = e.nextElement();
  455. String name = entry.getName();
  456. if (hasClassExtension(name))
  457. ret.add(new ZipEntryClassFile(this, entry));
  458. }
  459. // if (ret.isEmpty()) close();
  460. return ret;
  461. }
  462. private void ensureOpen() throws IOException {
  463. if (zipFile != null && openArchives.contains(zipFile)) {
  464. if (isReallyOpen())
  465. return;
  466. }
  467. if (openArchives.size() >= maxOpenArchives) {
  468. closeSomeArchives(openArchives.size() / 10); // Close 10% of
  469. // those open
  470. }
  471. zipFile = new ZipFile(file);
  472. if (!isReallyOpen()) {
  473. throw new FileNotFoundException("Can't open archive: " + file.getName() + " (size() check failed)");
  474. }
  475. openArchives.add(zipFile);
  476. }
  477. private boolean isReallyOpen() {
  478. try {
  479. zipFile.size(); // this will fail if the file has been closed
  480. // for
  481. // some reason;
  482. return true;
  483. } catch (IllegalStateException ex) {
  484. // this means the zip file is closed...
  485. return false;
  486. }
  487. }
  488. public void closeSomeArchives(int n) {
  489. for (int i = n - 1; i >= 0; i--) {
  490. ZipFile zf = openArchives.get(i);
  491. try {
  492. zf.close();
  493. } catch (IOException e) {
  494. e.printStackTrace();
  495. }
  496. openArchives.remove(i);
  497. }
  498. }
  499. public void close() {
  500. if (zipFile == null)
  501. return;
  502. try {
  503. openArchives.remove(zipFile);
  504. zipFile.close();
  505. } catch (IOException ioe) {
  506. throw new BCException("Can't close archive: " + file.getName(), ioe);
  507. } finally {
  508. zipFile = null;
  509. }
  510. }
  511. @Override
  512. public String toString() {
  513. return file.getName();
  514. }
  515. }
  516. /* private */static boolean hasClassExtension(String name) {
  517. return name.toLowerCase().endsWith((".class"));
  518. }
  519. public void closeArchives() {
  520. for (Entry entry : entries) {
  521. if (entry instanceof ZipFileEntry) {
  522. ((ZipFileEntry) entry).close();
  523. }
  524. openArchives.clear();
  525. }
  526. }
  527. // Copes with the security manager
  528. private static String getSystemPropertyWithoutSecurityException(String aPropertyName, String aDefaultValue) {
  529. try {
  530. return System.getProperty(aPropertyName, aDefaultValue);
  531. } catch (SecurityException ex) {
  532. return aDefaultValue;
  533. }
  534. }
  535. // Mainly exposed for testing
  536. public List<Entry> getEntries() {
  537. return entries;
  538. }
  539. }