|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package sun.misc; |
|
|
|
import java.io.BufferedReader; |
|
import java.io.FileReader; |
|
import java.io.File; |
|
import java.io.IOException; |
|
import java.util.ArrayList; |
|
import java.util.Collections; |
|
import java.util.HashMap; |
|
import java.util.List; |
|
import java.util.Map; |
|
|
|
/* |
|
* MetaIndex is intended to decrease startup time (in particular cold |
|
* start, when files are not yet in the disk cache) by providing a |
|
* quick reject mechanism for probes into jar files. The on-disk |
|
* representation of the meta-index is a flat text file with per-jar |
|
* entries indicating (generally speaking) prefixes of package names |
|
* contained in the jar. As an example, here is an edited excerpt of |
|
* the meta-index generated for jre/lib in the current build: |
|
* |
|
<PRE> |
|
% VERSION 1 |
|
# charsets.jar |
|
sun/ |
|
# jce.jar |
|
javax/ |
|
! jsse.jar |
|
sun/ |
|
com/sun/net/ |
|
javax/ |
|
com/sun/security/ |
|
@ resources.jar |
|
com/sun/xml/ |
|
com/sun/rowset/ |
|
com/sun/org/ |
|
sun/ |
|
com/sun/imageio/ |
|
javax/ |
|
com/sun/java/swing/ |
|
META-INF/services/ |
|
com/sun/java/util/jar/pack/ |
|
com/sun/corba/ |
|
com/sun/jndi/ |
|
! rt.jar |
|
org/w3c/ |
|
com/sun/imageio/ |
|
javax/ |
|
java/ |
|
sun/ |
|
... |
|
</PRE> |
|
* <p> A few notes about the design of the meta-index: |
|
* |
|
* <UL> |
|
* |
|
* <LI> It contains entries for multiple jar files. This is |
|
* intentional, to reduce the number of disk accesses that need to be |
|
* performed during startup. |
|
* |
|
* <LI> It is only intended to act as a fast reject mechanism to |
|
* prevent application and other classes from forcing all jar files on |
|
* the boot and extension class paths to be opened. It is not intended |
|
* as a precise index of the contents of the jar. |
|
* |
|
* <LI> It should be as small as possible to reduce the amount of time |
|
* required to parse it during startup. For example, adding on the |
|
* secondary package element to java/ and javax/ packages |
|
* ("javax/swing/", for example) causes the meta-index to grow |
|
* significantly. This is why substrings of the packages have been |
|
* chosen as the principal contents. |
|
* |
|
* <LI> It is versioned, and optional, to prevent strong dependencies |
|
* between the JVM and JDK. It is also potentially applicable to more |
|
* than just the boot and extension class paths. |
|
* |
|
* <LI> Precisely speaking, it plays different role in JVM and J2SE |
|
* side. On the JVM side, meta-index file is used to speed up locating the |
|
* class files only while on the J2SE side, meta-index file is used to speed |
|
* up the resources file & class file. |
|
* To help the JVM and J2SE code to better utilize the information in meta-index |
|
* file, we mark the jar file differently. Here is the current rule we use. |
|
* For jar file containing only class file, we put '!' before the jar file name; |
|
* for jar file containing only resources file, we put '@' before the jar file name; |
|
* for jar file containing both resources and class file, we put '#' before the |
|
* jar name. |
|
* Notice the fact that every jar file contains at least the manifest file, so when |
|
* we say "jar file containing only class file", we don't include that file. |
|
* |
|
* </UL> |
|
* |
|
* <p> To avoid changing the behavior of the current application |
|
* loader and other loaders, the current MetaIndex implementation in |
|
* the JDK requires that the directory containing the meta-index be |
|
* registered with the MetaIndex class before construction of the |
|
* associated URLClassPath. This prevents the need for automatic |
|
* searching for the meta-index in the URLClassPath code and potential |
|
* changes in behavior for non-core ClassLoaders. |
|
* |
|
* This class depends on make/tools/MetaIndex/BuildMetaIndex.java and |
|
* is used principally by sun.misc.URLClassPath. |
|
*/ |
|
|
|
public class MetaIndex { |
|
|
|
private static volatile Map<File, MetaIndex> jarMap; |
|
|
|
|
|
private String[] contents; |
|
|
|
|
|
private boolean isClassOnlyJar; |
|
|
|
//---------------------------------------------------------------------- |
|
// Registration of directories (which can cause parsing of the |
|
// meta-index file if it is present), and fetching of parsed |
|
// meta-indices |
|
// jarMap is not strictly thread-safe when the meta index mechanism |
|
// is extended for user-provided jar files in future. |
|
|
|
public static MetaIndex forJar(File jar) { |
|
return getJarMap().get(jar); |
|
} |
|
|
|
// 'synchronized' is added to protect the jarMap from being modified |
|
|
|
public static synchronized void registerDirectory(File dir) { |
|
// Note that this does not currently check to see whether the |
|
// directory has previously been registered, since the meta-index |
|
// in a particular directory creates multiple entries in the |
|
// jarMap. If this mechanism is extended beyond the boot and |
|
// extension class paths (for example, automatically searching for |
|
// meta-index files in directories containing jars which have been |
|
// explicitly opened) then this code should be generalized. |
|
// |
|
|
|
File indexFile = new File(dir, "meta-index"); |
|
if (indexFile.exists()) { |
|
try { |
|
BufferedReader reader = new BufferedReader(new FileReader(indexFile)); |
|
String line = null; |
|
String curJarName = null; |
|
boolean isCurJarContainClassOnly = false; |
|
List<String> contents = new ArrayList<String>(); |
|
Map<File, MetaIndex> map = getJarMap(); |
|
|
|
|
|
dir = dir.getCanonicalFile(); |
|
|
|
|
|
* before trying to parse this file. */ |
|
line = reader.readLine(); |
|
if (line == null || |
|
!line.equals("% VERSION 2")) { |
|
reader.close(); |
|
return; |
|
} |
|
while ((line = reader.readLine()) != null) { |
|
switch (line.charAt(0)) { |
|
case '!': |
|
case '#': |
|
case '@': { |
|
|
|
if ((curJarName != null) && (contents.size() > 0)) { |
|
map.put(new File(dir, curJarName), |
|
new MetaIndex(contents, |
|
isCurJarContainClassOnly)); |
|
|
|
contents.clear(); |
|
} |
|
|
|
curJarName = line.substring(2); |
|
if (line.charAt(0) == '!') { |
|
isCurJarContainClassOnly = true; |
|
} else if (isCurJarContainClassOnly) { |
|
isCurJarContainClassOnly = false; |
|
} |
|
|
|
break; |
|
} |
|
case '%': |
|
break; |
|
default: { |
|
contents.add(line); |
|
} |
|
} |
|
} |
|
|
|
if ((curJarName != null) && (contents.size() > 0)) { |
|
map.put(new File(dir, curJarName), |
|
new MetaIndex(contents, isCurJarContainClassOnly)); |
|
} |
|
|
|
reader.close(); |
|
|
|
} catch (IOException e) { |
|
// Silently fail for now (similar behavior to elsewhere in |
|
// extension and core loaders) |
|
} |
|
} |
|
} |
|
|
|
//---------------------------------------------------------------------- |
|
// Public APIs |
|
// |
|
|
|
public boolean mayContain(String entry) { |
|
// Ask non-class file from class only jar returns false |
|
// This check is important to avoid some class only jar |
|
|
|
if (isClassOnlyJar && !entry.endsWith(".class")){ |
|
return false; |
|
} |
|
|
|
String[] conts = contents; |
|
for (int i = 0; i < conts.length; i++) { |
|
if (entry.startsWith(conts[i])) { |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
|
|
//---------------------------------------------------------------------- |
|
// Implementation only below this point |
|
|
|
private MetaIndex(List<String> entries, boolean isClassOnlyJar) |
|
throws IllegalArgumentException { |
|
if (entries == null) { |
|
throw new IllegalArgumentException(); |
|
} |
|
|
|
contents = entries.toArray(new String[0]); |
|
this.isClassOnlyJar = isClassOnlyJar; |
|
} |
|
|
|
private static Map<File, MetaIndex> getJarMap() { |
|
if (jarMap == null) { |
|
synchronized (MetaIndex.class) { |
|
if (jarMap == null) { |
|
jarMap = new HashMap<File, MetaIndex>(); |
|
} |
|
} |
|
} |
|
assert jarMap != null; |
|
return jarMap; |
|
} |
|
} |