Download
Getting Started
Members
Projects
Community
Marketplace
Events
Planet Eclipse
Newsletter
Videos
Participate
Report a Bug
Forums
Mailing Lists
Wiki
IRC
How to Contribute
Working Groups
Automotive
Internet of Things
LocationTech
Long-Term Support
PolarSys
Science
OpenMDM
More
Community
Marketplace
Events
Planet Eclipse
Newsletter
Videos
Participate
Report a Bug
Forums
Mailing Lists
Wiki
IRC
How to Contribute
Working Groups
Automotive
Internet of Things
LocationTech
Long-Term Support
PolarSys
Science
OpenMDM
Toggle navigation
Bugzilla – Attachment 268342 Details for
Bug 516617
[JDK9]ClassPathManager$JImageEnry::find is too slow
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
Log In
[x]
|
Terms of Use
|
Copyright Agent
More Polished Version.
ClassPathManager.java (text/x-java), 21.82 KB, created by
Cleber Muramoto
on 2017-05-15 11:13:00 EDT
(
hide
)
Description:
More Polished Version.
Filename:
MIME Type:
Creator:
Cleber Muramoto
Created:
2017-05-15 11:13:00 EDT
Size:
21.82 KB
patch
obsolete
>/* > * ******************************************************************* Copyright (c) 2002 Palo Alto > * Research Center, Incorporated (PARC). All rights reserved. This program and the accompanying > * materials are made available under the terms of the Eclipse Public License v1.0 which accompanies > * this distribution and is available at http://www.eclipse.org/legal/epl-v10.html Contributors: > * PARC initial implementation > ******************************************************************/ > >package org.aspectj.weaver.bcel; > >import java.io.ByteArrayInputStream; >import java.io.File; >import java.io.FileInputStream; >import java.io.FileNotFoundException; >import java.io.IOException; >import java.io.InputStream; >import java.io.RandomAccessFile; >import java.io.UncheckedIOException; >import java.net.URI; >import java.nio.ByteBuffer; >import java.nio.MappedByteBuffer; >import java.nio.channels.FileChannel; >import java.nio.channels.FileChannel.MapMode; >import java.nio.charset.Charset; >import java.nio.file.DirectoryStream; >import java.nio.file.FileSystem; >import java.nio.file.FileSystems; >import java.nio.file.FileVisitResult; >import java.nio.file.FileVisitor; >import java.nio.file.Files; >import java.nio.file.NoSuchFileException; >import java.nio.file.Path; >import java.nio.file.Paths; >import java.nio.file.attribute.BasicFileAttributes; >import java.util.ArrayList; >import java.util.Arrays; >import java.util.Comparator; >import java.util.Enumeration; >import java.util.Iterator; >import java.util.List; >import java.util.Map; >import java.util.Optional; >import java.util.TreeMap; >import java.util.zip.ZipEntry; >import java.util.zip.ZipFile; > >import org.aspectj.bridge.IMessageHandler; >import org.aspectj.bridge.MessageUtil; >import org.aspectj.util.LangUtil; >import org.aspectj.weaver.BCException; >import org.aspectj.weaver.UnresolvedType; >import org.aspectj.weaver.WeaverMessages; >import org.aspectj.weaver.tools.Trace; >import org.aspectj.weaver.tools.TraceFactory; > >public class ClassPathManager { > > static final class BBIS extends InputStream { > > final ByteBuffer src; > > private BBIS(final ByteBuffer src) { > super(); > this.src = src; > } > > @Override > public int available() { > return src.remaining(); > } > > @Override > public void close() { > } > > @Override > public void mark(final int readlimit) { > src.mark(); > } > > @Override > public boolean markSupported() { > return true; > } > > @Override > public int read() { > return src.hasRemaining() ? src.get() & 0xFF : -1; > } > > @Override > public int read(final byte[] b) { > return read(b, 0, b.length); > } > > @Override > public int read(final byte[] b, final int off, final int len) { > if (b == null || off < 0 || len < 0) { > throw new IllegalArgumentException(); > } > final ByteBuffer bb = src; > > if (!bb.hasRemaining()) { > return -1; > } > > final int l = Math.min(Math.min(b.length - off, len), bb.remaining()); > > if (l > 0) { > bb.get(b, off, l); > } > > return l; > } > > @Override > public void reset() { > src.reset(); > } > > @Override > public long skip(final long n) { > final int b = (int) n; > > if (b < 0) { > return -1l; > } > > int k = src.remaining(); > > if (b < k) { > k = b < 0 ? 0 : b; > } > > src.position(src.position() + k); > > return k; > } > } > > private static class ByteBasedClassFile extends ClassFile { > > private byte[] bytes; > private ByteArrayInputStream bais; > private String path; > > public ByteBasedClassFile(byte[] bytes, String path) { > this.bytes = bytes; > this.path = path; > } > > @Override > public void close() { > if (this.bais != null) { > try { > this.bais.close(); > } catch (IOException e) { > } > this.bais = null; > } > } > > @Override > public InputStream getInputStream() throws IOException { > this.bais = new ByteArrayInputStream(bytes); > return this.bais; > } > > @Override > public String getPath() { > return this.path; > } > > } > > private static class ByteBufferBasedClassFile extends ClassFile { > private String path; > private ByteBuffer data; > > public ByteBufferBasedClassFile(ByteBuffer data, String path) { > super(); > this.data = data; > this.path = path; > } > > @Override > public void close() { > > } > > @Override > public InputStream getInputStream() throws IOException { > return new BBIS(data); > } > > @Override > public String getPath() { > return path; > } > } > > public static class CachedJImageEntry extends Entry implements FileVisitor<Path>, Comparator<byte[]> { > > final ThreadLocal<byte[]> chunks; > final Charset cs; > RandomAccessFile raf; > FileChannel ch; > MappedByteBuffer data; > int sz; > > // Used during cache construction only > TreeMap<byte[], Path> types; > > public CachedJImageEntry() throws IOException { > this.cs = Charset.forName("UTF-8"); > this.chunks = new ThreadLocal<>(); > > String name = String.format(".aj.cache.%s.%s", System.getProperty("java.vm.specification.vendor", "ukn").replaceAll("[^\\w]+", "_"), System.getProperty("java.runtime.version", "9").replaceAll("[^\\w]+", "_")); > > Path cache = Paths.get(System.getProperty("user.home"), name); > > if (!Files.exists(cache)) { > doCache(cache); > } > > loadCache(cache); > } > > private byte[] buffer(int len, int offset) throws IOException { > byte[] chunk = chunks.get(); > > if (chunk == null || len > chunk.length) { > chunks.set(chunk = new byte[len + 128]); > } > > ch.read(ByteBuffer.wrap(chunk, 0, len), offset); > > return chunk; > } > > private byte[] buffer(MappedByteBuffer data, int len, int offset) { > byte[] chunk = chunks.get(); > > if (chunk == null || len > chunk.length) { > chunks.set(chunk = new byte[len + 128]); > } > > data.duplicate().position(offset).get(chunk, 0, len); > return chunk; > } > > @Override > public int compare(byte[] l, byte[] r) { > return Arrays.compare(l, r); > } > > private void doCache(Path cache) throws IOException { > long now = System.currentTimeMillis(); > trace.info("Creating JImage cache"); > this.types = new TreeMap<>(this); > > try { > FileSystem fs = FileSystems.getFileSystem(JRT_URI); > Iterable<java.nio.file.Path> roots = fs.getRootDirectories(); > > for (java.nio.file.Path path : roots) { > try (DirectoryStream<java.nio.file.Path> stream = Files.newDirectoryStream(path)) { > for (java.nio.file.Path module : stream) { > Files.walkFileTree(module, this); > } > } > } > > try (RandomAccessFile raf = new RandomAccessFile(cache.toFile(), "rw")) { > int ts = types.size(); > raf.writeInt(ts); > > int offset = 5 + ts * 12; > for (Map.Entry<byte[], Path> e : types.entrySet()) { > > byte[] key = e.getKey(); > byte[] value = Files.readAllBytes(e.getValue()); > > raf.writeInt(key.length); > raf.writeInt(value.length); > raf.writeInt(offset); > > long pos = raf.getFilePointer(); > > raf.seek(offset); > raf.write(key); > raf.write(value); > raf.seek(offset); > offset += key.length + value.length; > > raf.seek(pos); > } > } > } finally { > this.types.clear(); > this.types = null; > } > trace.info(String.format("Created JImage Cache @%s in (%dms)", cache, System.currentTimeMillis() - now)); > } > > @Override > public ClassFile find(String name) throws IOException { > ByteBuffer slice = seek(name.getBytes(cs)); > > return slice == null ? null : new ByteBufferBasedClassFile(slice, name); > } > > private void loadCache(Path cache) throws FileNotFoundException, IOException { > long now = System.currentTimeMillis(); > RandomAccessFile raf = new RandomAccessFile(cache.toFile(), "rw"); > > int sz = this.sz = raf.readInt(); > this.raf = raf; > this.ch = raf.getChannel(); > data = JIMAGE_CACHE_STRATEGY == 1 ? ch.map(MapMode.READ_ONLY, 0, raf.length()) : null; > > long elapsed = System.currentTimeMillis() - now; > trace.info(String.format("Loaded (%s:%dMB/%d Entries) JImage cache from %s in (%dms).", data == null ? "Raf" : "Mapped", raf.length() / 1024 / 1024, sz, cache, elapsed)); > } > > @Override > public FileVisitResult postVisitDirectory(Path p, IOException arg1) throws IOException { > return FileVisitResult.CONTINUE; > } > > @Override > public FileVisitResult preVisitDirectory(Path p, BasicFileAttributes arg1) throws IOException { > return FileVisitResult.CONTINUE; > } > > private ByteBuffer seek(byte[] key) { > MappedByteBuffer data = this.data; > > return data != null ? seek(data, key) : seek(ch, key); > > } > > private ByteBuffer seek(FileChannel ch, byte[] key) { > int low = 0; > int high = sz - 1; > > ByteBuffer bb = ByteBuffer.allocate(4); > try { > while (low <= high) { > int mid = low + high >>> 1; > int ix = 4 + mid * 12; > ch.read(bb.clear(), ix); > int len = bb.flip().getInt(); > ch.read(bb.clear(), ix + 8); > int offset = bb.flip().getInt(); > > byte[] buffer = buffer(len, offset); > > int cmp = Arrays.compare(buffer, 0, len, key, 0, key.length); > > if (cmp < 0) { > low = mid + 1; > } else if (cmp > 0) { > high = mid - 1; > } else { > offset += len; > ch.read(bb.clear(), ix + 4); > > len = bb.flip().getInt(); > > ByteBuffer data = ByteBuffer.allocate(len); > int r; > while ((r = ch.read(data, offset)) > 0) { > offset += r; > } > > return data.flip(); > } > } > } catch (IOException e) { > throw new UncheckedIOException(e); > } > > return null; > } > > private ByteBuffer seek(MappedByteBuffer data, byte[] key) { > int low = 0; > int high = sz - 1; > > while (low <= high) { > int mid = low + high >>> 1; > int ix = 4 + mid * 12; > int len = data.getInt(ix); > int offset = data.getInt(ix + 8); > > byte[] buffer = buffer(data, len, offset); > > int cmp = Arrays.compare(buffer, 0, len, key, 0, key.length); > > if (cmp < 0) { > low = mid + 1; > } else if (cmp > 0) { > high = mid - 1; > } else { > offset += len; > len = data.getInt(ix + 4); > > return data.duplicate().position(offset).limit(len + offset).slice(); > } > } > > return null; > } > > @Override > public FileVisitResult visitFile(Path p, BasicFileAttributes attr) throws IOException { > > String name = p.toString(); > if (name.endsWith(".class")) { > int r = name.indexOf('/', name.indexOf('/', 1) + 1); > String typeName = name.substring(r + 1).replace('/', '.').replace(".class", ""); > types.put(typeName.getBytes(cs), p); > } > > return FileVisitResult.CONTINUE; > } > > @Override > public FileVisitResult visitFileFailed(Path arg0, IOException ex) throws IOException { > throw ex; > } > } > > public abstract static class ClassFile { > public abstract void close(); > > public abstract InputStream getInputStream() throws IOException; > > public abstract String getPath(); > } > > public class DirEntry extends Entry { > private String dirPath; > > public DirEntry(File dir) { > this.dirPath = dir.getPath(); > } > > public DirEntry(String dirPath) { > this.dirPath = dirPath; > } > > @Override > public ClassFile find(String name) { > File f = new File(dirPath + File.separator + name.replace('.', File.separatorChar) + ".class"); > if (f.isFile()) { > return new FileClassFile(f); > } else { > return null; > } > } > > @SuppressWarnings("rawtypes") > public List getAllClassFiles() { > throw new RuntimeException("unimplemented"); > } > > @Override > public String toString() { > return dirPath; > } > } > > public abstract static class Entry { > public abstract ClassFile find(String name) throws IOException; > > // public abstract List getAllClassFiles() throws IOException; > } > > private static class FileClassFile extends ClassFile { > private File file; > private FileInputStream fis; > > public FileClassFile(File file) { > this.file = file; > } > > @Override > public void close() { > try { > if (fis != null) { > fis.close(); > } > } catch (IOException ioe) { > throw new BCException("Can't close class file : " + file.getName(), ioe); > } finally { > fis = null; > } > } > > @Override > public InputStream getInputStream() throws IOException { > fis = new FileInputStream(file); > return fis; > } > > @Override > public String getPath() { > return file.getPath(); > } > } > > public class JImageEntry extends Entry { > private FileSystem fs; > > public JImageEntry(File file) { > fs = FileSystems.getFileSystem(JRT_URI); > // Iterable<java.nio.file.Path> roots = fs.getRootDirectories(); > // java.nio.file.Path basePath = null; > // try { > // System.err.println("Find on javax.naming.Context: > // "+find("javax.naming.Context")); > // } catch (IOException e) { > // // TODO Auto-generated catch block > // e.printStackTrace(); > // } > // roots: for (java.nio.file.Path path : roots) { > // System.err.println(">>"+path); > // try (DirectoryStream<java.nio.file.Path> stream = > // Files.newDirectoryStream(path)) { > // for (java.nio.file.Path subdir: stream) { > // System.err.println(">>>"+subdir); > //// if (subdir.toString().indexOf(JAVA_BASE_PATH) != -1) { > //// basePath = subdir; > //// break roots; > //// } > // } > // } catch (Exception e) { > // e.printStackTrace(); > // } > // } > } > > @Override > public ClassFile find(String name) throws IOException { > String fileName = name.replace('.', '/') + ".class"; > try { > // /modules/java.base/java/lang/Object.class (jdk9 b74) > Path p = fs.getPath(MODULES_PATH, JAVA_BASE_PATH, fileName); > byte[] bs = Files.readAllBytes(p); > return new ByteBasedClassFile(bs, fileName); > } catch (NoSuchFileException nsfe) { > // try other modules! > Iterable<java.nio.file.Path> roots = fs.getRootDirectories(); > for (java.nio.file.Path path : roots) { > DirectoryStream<java.nio.file.Path> stream = Files.newDirectoryStream(path); > try { > for (java.nio.file.Path module : stream) { > // module will be something like /packages or > // /modules > for (java.nio.file.Path submodule : Files.newDirectoryStream(module)) { > // submodule will be /modules/java.base or > // somesuch > try { > Path p = fs.getPath(submodule.toString(), fileName); > byte[] bs = Files.readAllBytes(p); > return new ByteBasedClassFile(bs, fileName); > } catch (NoSuchFileException nsfe2) { > } > } > } > } finally { > stream.close(); > } > } > return null; > } > } > > public ClassFile find(String module, String name) throws IOException { > String fileName = name.replace('.', '/') + ".class"; > try { > Path p = fs.getPath(module, fileName); > byte[] bs = Files.readAllBytes(p); > return new ByteBasedClassFile(bs, fileName); > } catch (NoSuchFileException nsfe) { > return null; > } > } > > } > > private static class ZipEntryClassFile extends ClassFile { > private ZipEntry entry; > private ZipFileEntry zipFile; > private InputStream is; > > public ZipEntryClassFile(ZipFileEntry zipFile, ZipEntry entry) { > this.zipFile = zipFile; > this.entry = entry; > } > > @Override > public void close() { > try { > if (is != null) { > is.close(); > } > } catch (IOException e) { > e.printStackTrace(); > } finally { > is = null; > } > } > > @Override > public InputStream getInputStream() throws IOException { > is = zipFile.getZipFile().getInputStream(entry); > return is; > } > > @Override > public String getPath() { > return entry.getName(); > } > > } > > public class ZipFileEntry extends Entry { > private File file; > private ZipFile zipFile; > > public ZipFileEntry(File file) throws IOException { > this.file = file; > } > > public ZipFileEntry(ZipFile zipFile) { > this.zipFile = zipFile; > } > > public void close() { > if (zipFile == null) { > return; > } > try { > openArchives.remove(zipFile); > zipFile.close(); > } catch (IOException ioe) { > throw new BCException("Can't close archive: " + file.getName(), ioe); > } finally { > zipFile = null; > } > } > > public void closeSomeArchives(int n) { > for (int i = n - 1; i >= 0; i--) { > ZipFile zf = (ZipFile) openArchives.get(i); > try { > zf.close(); > } catch (IOException e) { > e.printStackTrace(); > } > openArchives.remove(i); > } > } > > private void ensureOpen() throws IOException { > if (zipFile != null && openArchives.contains(zipFile)) { > if (isReallyOpen()) { > return; > } > } > if (openArchives.size() >= maxOpenArchives) { > closeSomeArchives(openArchives.size() / 10); // Close 10% of > // those open > } > zipFile = new ZipFile(file); > if (!isReallyOpen()) { > throw new FileNotFoundException("Can't open archive: " + file.getName() + " (size() check failed)"); > } > openArchives.add(zipFile); > } > > @Override > public ClassFile find(String name) throws IOException { > ensureOpen(); > String key = name.replace('.', '/') + ".class"; > ZipEntry entry = zipFile.getEntry(key); > if (entry != null) { > return new ZipEntryClassFile(this, entry); > } else { > return null; // This zip will be closed when necessary... > } > } > > public List<ZipEntryClassFile> getAllClassFiles() throws IOException { > ensureOpen(); > List<ZipEntryClassFile> ret = new ArrayList<ZipEntryClassFile>(); > for (Enumeration<? extends ZipEntry> e = zipFile.entries(); e.hasMoreElements();) { > ZipEntry entry = e.nextElement(); > String name = entry.getName(); > if (hasClassExtension(name)) { > ret.add(new ZipEntryClassFile(this, entry)); > } > } > // if (ret.isEmpty()) close(); > return ret; > } > > public ZipFile getZipFile() { > return zipFile; > } > > private boolean isReallyOpen() { > try { > zipFile.size(); // this will fail if the file has been closed > // for > // some reason; > return true; > } catch (IllegalStateException ex) { > // this means the zip file is closed... > return false; > } > > } > > @Override > public String toString() { > return file.getName(); > } > } > > private static int maxOpenArchives = -1; > private static final int MAXOPEN_DEFAULT = 1000; > > private static Trace trace = TraceFactory.getTraceFactory().getTrace(ClassPathManager.class); > > private static final int JIMAGE_CACHE_STRATEGY; > > private static Optional<Entry> CACHED_ENTRY; > > private static URI JRT_URI = URI.create("jrt:/"); //$NON-NLS-1$ > > private static String MODULES_PATH = "modules"; //$NON-NLS-1$ > > private static String JAVA_BASE_PATH = "java.base"; //$NON-NLS-1$ > > static { > String openzipsString = getSystemPropertyWithoutSecurityException("org.aspectj.weaver.openarchives", Integer.toString(MAXOPEN_DEFAULT)); > maxOpenArchives = Integer.parseInt(openzipsString); > if (maxOpenArchives < 20) { > maxOpenArchives = 1000; > } > > JIMAGE_CACHE_STRATEGY = Integer.getInteger("org.aspectj.weaver.bcel.ClassPathManager.JIMAGE_CACHE_STRATEGY", 1); > } > > // /** > // * This method is extremely expensive and should only be called rarely > // */ > // public List getAllClassFiles() { > // List ret = new ArrayList(); > // for (Iterator i = entries.iterator(); i.hasNext(); ) { > // Entry entry = (Entry)i.next(); > // try { > // ret.addAll(entry.getAllClassFiles()); > // } catch (IOException e) { > // i.remove(); > // } > // } > // return ret; > // } > // > > // Copes with the security manager > private static String getSystemPropertyWithoutSecurityException(String aPropertyName, String aDefaultValue) { > try { > return System.getProperty(aPropertyName, aDefaultValue); > } catch (SecurityException ex) { > return aDefaultValue; > } > } > > /* private */static boolean hasClassExtension(String name) { > return name.toLowerCase().endsWith(".class"); > } > > private List<Entry> entries; > > // In order to control how many open files we have, we maintain a list. > // The max number is configured through the property: > // org.aspectj.weaver.openarchives > // and it defaults to 1000 > private List<ZipFile> openArchives = new ArrayList<ZipFile>(); > > protected ClassPathManager() { > } > > public ClassPathManager(List<String> classpath, IMessageHandler handler) { > if (trace.isTraceEnabled()) { > trace.enter("<init>", this, new Object[]{ classpath, handler }); > } > entries = new ArrayList<Entry>(); > for (String name : classpath) { > addPath(name, handler); > } > if (trace.isTraceEnabled()) { > trace.exit("<init>"); > } > } > > public void addPath(String name, IMessageHandler handler) { > File f = new File(name); > String lc = name.toLowerCase(); > if (!f.isDirectory()) { > if (!f.isFile()) { > if (!lc.endsWith(".jar") || lc.endsWith(".zip")) { > // heuristic-only: ending with .jar or .zip means probably a > // zip file > MessageUtil.info(handler, WeaverMessages.format(WeaverMessages.ZIPFILE_ENTRY_MISSING, name)); > } else { > MessageUtil.info(handler, WeaverMessages.format(WeaverMessages.DIRECTORY_ENTRY_MISSING, name)); > } > return; > } > try { > if (lc.endsWith(LangUtil.JRT_FS)) { > // Java9 > if (CACHED_ENTRY == null) { > if (JIMAGE_CACHE_STRATEGY != 0) { > try { > CACHED_ENTRY = Optional.of(new CachedJImageEntry()); > } catch (IOException ex) { > CACHED_ENTRY = Optional.empty(); > } > } else { > CACHED_ENTRY = Optional.empty(); > } > } > > Entry e = CACHED_ENTRY.orElseGet(() -> new JImageEntry(new File(f.getParentFile() + File.separator + "lib" + File.separator + "modules"))); > > entries.add(e); > } else { > entries.add(new ZipFileEntry(f)); > } > } catch (IOException ioe) { > MessageUtil.warn(handler, WeaverMessages.format(WeaverMessages.ZIPFILE_ENTRY_INVALID, name, ioe.getMessage())); > return; > } > } else { > entries.add(new DirEntry(f)); > } > } > > public void closeArchives() { > for (Entry entry : entries) { > if (entry instanceof ZipFileEntry) { > ((ZipFileEntry) entry).close(); > } > openArchives.clear(); > } > } > > public ClassFile find(UnresolvedType type) { > String name = type.getName(); > for (Iterator<Entry> i = entries.iterator(); i.hasNext();) { > Entry entry = i.next(); > try { > ClassFile ret = entry.find(name); > if (ret != null) { > return ret; > } > } catch (IOException ioe) { > // this is NOT an error: it's valid to have missing classpath > // entries > ioe.printStackTrace(); > i.remove(); > } > > } > return null; > } > > @Override > public String toString() { > StringBuffer buf = new StringBuffer(); > boolean start = true; > for (Entry entry : entries) { > if (start) { > start = false; > } else { > buf.append(File.pathSeparator); > } > buf.append(entry); > } > return buf.toString(); > } >}
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Raw
Actions:
View
Attachments on
bug 516617
:
268326
| 268342