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 268326 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
Caching lookups for jfs-rt resources
ClassPathManager.java (text/x-java), 19.72 KB, created by
Cleber Muramoto
on 2017-05-13 15:31:04 EDT
(
hide
)
Description:
Caching lookups for jfs-rt resources
Filename:
MIME Type:
Creator:
Cleber Muramoto
Created:
2017-05-13 15:31:04 EDT
Size:
19.72 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.net.URI; >import java.nio.ByteBuffer; >import java.nio.MappedByteBuffer; >import java.nio.channels.FileChannel.MapMode; >import java.nio.channels.ReadableByteChannel; >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.Enumeration; >import java.util.HashMap; >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 { > > 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>(); > private static int maxOpenArchives = -1; > private static final int MAXOPEN_DEFAULT = 1000; > > private static Trace trace = TraceFactory.getTraceFactory().getTrace(ClassPathManager.class); > > static { > String openzipsString = getSystemPropertyWithoutSecurityException("org.aspectj.weaver.openarchives", > Integer.toString(MAXOPEN_DEFAULT)); > maxOpenArchives = Integer.parseInt(openzipsString); > if (maxOpenArchives < 20) > maxOpenArchives = 1000; > } > > public ClassPathManager(List<String> classpath, IMessageHandler handler) { > if (trace.isTraceEnabled()) > trace.enter("<init>", this, new Object[] { classpath, handler }); > entries = new ArrayList<Entry>(); > for (Iterator<String> i = classpath.iterator(); i.hasNext();) { > String name = i.next(); > addPath(name, handler); > } > if (trace.isTraceEnabled()) > trace.exit("<init>"); > } > > protected ClassPathManager() { > } > > 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$ > > 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) { > try { > CACHED_ENTRY = Optional.of(new CachedJImageEntry()); > } catch (IOException ex) { > 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 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; > } > > public String toString() { > StringBuffer buf = new StringBuffer(); > boolean start = true; > for (Iterator<Entry> i = entries.iterator(); i.hasNext();) { > if (start) { > start = false; > } else { > buf.append(File.pathSeparator); > } > buf.append(i.next()); > } > return buf.toString(); > } > > // /** > // * 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; > // } > // > > public abstract static class ClassFile { > public abstract InputStream getInputStream() throws IOException; > > public abstract String getPath(); > > public abstract void close(); > } > > public abstract static class Entry { > public abstract ClassFile find(String name) throws IOException; > > // public abstract List getAllClassFiles() throws IOException; > } > > static final class BBIS extends InputStream { > > static final class Channel implements ReadableByteChannel { > > final ByteBuffer src; > > public Channel(final ByteBuffer src) { > super(); > this.src = src; > } > > @Override > public void close() { > > } > > @Override > public boolean isOpen() { > return src.hasRemaining(); > } > > @Override > public int read(final ByteBuffer dst) { > final ByteBuffer bb = this.src; > > if (bb == dst) { > throw new IllegalArgumentException(); > } > > int sr = bb.remaining(); > > if (sr > 0) { > final int dr = dst.remaining(); > > if (dr >= sr) { > dst.put(bb); > } else { > sr = dr; > if (sr > 0) { > final int l = bb.limit(); > bb.limit(bb.position() + sr); > dst.put(bb); > bb.limit(l); > } > } > } else { > sr = -1; > } > > return sr; > } > > } > > 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; > } > > public ReadableByteChannel newChannel() { > return newChannel(0, src.capacity()); > } > > public ReadableByteChannel newChannel(final int offset, final int len) { > final ByteBuffer buffer = this.src.duplicate(); > > buffer.position(offset).limit(offset + len); > > return new Channel(buffer); > } > > @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 ByteBufferBasedClassFile extends ClassFile { > private String path; > private ByteBuffer data; > > public ByteBufferBasedClassFile(ByteBuffer data, String path) { > super(); > this.data = data; > this.path = path; > } > > @Override > public InputStream getInputStream() throws IOException { > return new BBIS(data); > } > > @Override > public String getPath() { > return path; > } > > @Override > public void close() { > > } > } > > 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 InputStream getInputStream() throws IOException { > this.bais = new ByteArrayInputStream(bytes); > return this.bais; > } > > @Override > public String getPath() { > return this.path; > } > > @Override > public void close() { > if (this.bais != null) { > try { > this.bais.close(); > } catch (IOException e) { > } > this.bais = null; > } > } > > } > > private static class FileClassFile extends ClassFile { > private File file; > private FileInputStream fis; > > public FileClassFile(File file) { > this.file = file; > } > > public InputStream getInputStream() throws IOException { > fis = new FileInputStream(file); > return fis; > } > > 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; > } > } > > public String getPath() { > return file.getPath(); > } > } > > public class DirEntry extends Entry { > private String dirPath; > > public DirEntry(File dir) { > this.dirPath = dir.getPath(); > } > > public DirEntry(String dirPath) { > this.dirPath = dirPath; > } > > 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; > } > > public List getAllClassFiles() { > throw new RuntimeException("unimplemented"); > } > > public String toString() { > return dirPath; > } > } > > 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; > } > > public InputStream getInputStream() throws IOException { > is = zipFile.getZipFile().getInputStream(entry); > return is; > } > > public void close() { > try { > if (is != null) > is.close(); > } catch (IOException e) { > e.printStackTrace(); > } finally { > is = null; > } > } > > public String getPath() { > return entry.getName(); > } > > } > > public static class CachedJImageEntry extends Entry { > > RandomAccessFile raf; > Map<String, int[]> lookups; > MappedByteBuffer data; > > public CachedJImageEntry() throws IOException { > > Path cache = Paths.get(System.getProperty("user.home"), "aj.jdk9.cache"); > > if (!Files.exists(cache)) { > doCache(cache); > } > > loadCache(cache); > } > > private void loadCache(Path cache) throws FileNotFoundException, IOException { > trace.info("Loading JImage cache"); > RandomAccessFile raf = new RandomAccessFile(cache.toFile(), "rw"); > int sz = raf.readInt(); > // int dataOffset = (int) raf.readLong(); > > Map<String, int[]> lookups = new HashMap<>(); > > for (int j = 0; j < sz; j++) { > lookups.put(raf.readUTF(), new int[] { raf.readInt(), raf.readInt() }); > } > > this.lookups = lookups; > this.raf = raf; > data = raf.getChannel().map(MapMode.READ_ONLY, raf.getFilePointer(), raf.length() - raf.getFilePointer()); > > trace.info("Loaded JImage cache"); > } > > private void doCache(Path cache) throws IOException { > long now = System.currentTimeMillis(); > trace.info("Creating JImage cache"); > > FileSystem fs = FileSystems.getFileSystem(JRT_URI); > Iterable<java.nio.file.Path> roots = fs.getRootDirectories(); > > Map<String, Path> types = new TreeMap<>(); > > 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, new FileVisitor<Path>() { > > @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; > } > > @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, p); > } > > return FileVisitResult.CONTINUE; > } > > @Override > public FileVisitResult visitFileFailed(Path arg0, IOException ex) throws IOException { > throw ex; > } > }); > } > } > > try (RandomAccessFile raf = new RandomAccessFile(cache.toFile(), "rw")) { > > raf.writeInt(types.size()); > > int offset = 0; > for (Map.Entry<String, Path> e : types.entrySet()) { > int sz = (int) Files.size(e.getValue()); > > raf.writeUTF(e.getKey()); > raf.writeInt(sz); > raf.writeInt(offset); > > offset += sz; > } > > for (Path e : types.values()) { > raf.write(Files.readAllBytes(e)); > } > } > } > > trace.info("Created JImage cache in " + (System.currentTimeMillis() - now)); > } > > @Override > public ClassFile find(String name) throws IOException { > > int[] ptr = lookups.get(name); > > ClassFile rv; > if (ptr != null) { > ByteBuffer slice = data.duplicate().position(ptr[1]).limit(ptr[0] + ptr[1]).slice(); > > rv = new ByteBufferBasedClassFile(slice, name); > } else { > rv = null; > } > > return rv; > } > } > > 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; > } > } > > } > > 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 ZipFile getZipFile() { > return zipFile; > } > > 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; > } > > 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); > } > > 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; > } > > } > > 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); > } > } > > 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 String toString() { > return file.getName(); > } > } > > /* private */static boolean hasClassExtension(String name) { > return name.toLowerCase().endsWith((".class")); > } > > public void closeArchives() { > for (Entry entry : entries) { > if (entry instanceof ZipFileEntry) { > ((ZipFileEntry) entry).close(); > } > openArchives.clear(); > } > } > > // Copes with the security manager > private static String getSystemPropertyWithoutSecurityException(String aPropertyName, String aDefaultValue) { > try { > return System.getProperty(aPropertyName, aDefaultValue); > } catch (SecurityException ex) { > return aDefaultValue; > } > } >}
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