Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/make/src/classes/build/tools/buildmetaindex/BuildMetaIndex.java
32287 views
/*1* Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package build.tools.buildmetaindex;2627import java.io.*;28import java.util.*;29import java.util.jar.*;3031/** Constructs a meta-index of the specified jar files. The meta-index32contains prefixes of packages contained in these jars, indexed by33the jar file name. It is intended to be consumed by the JVM to34allow the boot class loader to be made lazier. For example, when35class data sharing is enabled, the presence of the meta-index36allows the JVM to skip opening rt.jar if all of the dependent37classes of the application are in the shared archive. A similar38mechanism could be useful at the application level as well, for39example to make the extension class loader lazier.4041<p> The contents of the meta-index file for jre/lib look something42like this:4344<PRE>45% VERSION 246# charsets.jar47sun/48# jce.jar49javax/50! jsse.jar51sun/52com/sun/net/53javax/54com/sun/security/55# management-agent.jar56! rt.jar57org/w3c/58com/sun/image/59com/sun/org/60com/sun/imageio/61com/sun/accessibility/62javax/63...64</PRE>6566<p> It is a current invariant of the code in the JVM which67consumes the meta-index that the meta-index indexes only jars in68one directory. It is acceptable for jars in that directory to not69be mentioned in the meta-index. The meta-index is designed more to70be able to perform a quick rejection test of the presence of a71particular class in a particular jar file than to be a precise72index of the contents of the jar. */7374public class BuildMetaIndex {75public static void main(String[] args) throws IOException {76/* The correct usage of this class is as following:77* java BuildMetaIndex -o <meta-index> <a list of jar files>78* So the argument length should be at least 3 and the first argument should79* be '-o'.80*/81if (args.length < 3 ||82!args[0].equals("-o")) {83printUsage();84System.exit(1);85}8687try {88PrintStream out = new PrintStream(new FileOutputStream(args[1]));89out.println("% VERSION 2");90out.println("% WARNING: this file is auto-generated; do not edit");91out.println("% UNSUPPORTED: this file and its format may change and/or");92out.println("% may be removed in a future release");93for (int i = 2; i < args.length; i++) {94String filename = args[i];95JarMetaIndex jmi = new JarMetaIndex(filename);96HashSet<String> index = jmi.getMetaIndex();97if (index == null) {98continue;99}100/*101* meta-index file plays different role in JVM and JDK side.102* On the JVM side, meta-index file is used to speed up locating the103* class files only while on the JDK side, meta-index file is used to speed104* up the resources file and class file.105* To help the JVM and JDK code to better utilize the information in meta-index106* file, we mark the jar file differently. Here is the current rule we use (See107* JarFileKind.getMarkChar() method. )108* For jar file containing only class file, we put '!' before the jar file name;109* for jar file containing only resources file, we put '@' before the jar file name;110* for jar file containing both resources and class file, we put '#' before the jar name.111* Notice the fact that every jar file contains at least the manifest file, so when112* we say "jar file containing only class file", we don't include that file.113*/114115out.println(jmi.getJarFileKind().getMarkerChar() + " " + filename);116for (String entry : index) {117out.println(entry);118}119120}121out.flush();122out.close();123} catch (FileNotFoundException fnfe) {124System.err.println("FileNotFoundException occurred");125System.exit(2);126}127}128129private static void printUsage() {130String usage =131"BuildMetaIndex is used to generate a meta index file for the jar files\n" +132"you specified. The following is its usage:\n" +133" java BuildMetaIndex -o <the output meta index file> <a list of jar files> \n" +134" You can specify *.jar to refer to all the jar files in the current directory";135136System.err.println(usage);137}138}139140enum JarFileKind {141142CLASSONLY ('!'),143RESOURCEONLY ('@'),144MIXED ('#');145146private char markerChar;147148JarFileKind(char markerChar) {149this.markerChar = markerChar;150}151152public char getMarkerChar() {153return markerChar;154}155}156157/*158* JarMetaIndex associates the jar file with a set of what so called159* "meta-index" of the jar file. Essentially, the meta-index is a list160* of class prefixes and the plain files contained in META-INF directory (161* not include the manifest file itself). This will help sun.misc.URLClassPath162* to quickly locate the resource file and hotspot VM to locate the class file.163*164*/165class JarMetaIndex {166private JarFile jar;167private volatile HashSet<String> indexSet;168169/*170* A hashmap contains a mapping from the prefix string to171* a hashset which contains a set of the second level of prefix string.172*/173private HashMap<String, HashSet<String>> knownPrefixMap = new HashMap<>();174175/**176* Special value for the HashSet to indicate that there are classes in177* the top-level package.178*/179private static final String TOP_LEVEL = "TOP";180181/*182* A class for mapping package prefixes to the number of183* levels of package elements to include.184*/185static class ExtraLevel {186public ExtraLevel(String prefix, int levels) {187this.prefix = prefix;188this.levels = levels;189}190String prefix;191int levels;192}193194/*195* A list of the special-cased package names.196*/197private static ArrayList<ExtraLevel> extraLevels = new ArrayList<>();198199static {200// The order of these statements is significant,201// since we stop looking after the first match.202203// Need more precise information to disambiguate204// (illegal) references from applications to205// obsolete backported collections classes in206// com/sun/java/util207extraLevels.add(new ExtraLevel("com/sun/java/util/", Integer.MAX_VALUE));208extraLevels.add(new ExtraLevel("com/sun/java/", 4));209// Need more information than just first two package210// name elements to determine that classes in211// deploy.jar are not in rt.jar212extraLevels.add(new ExtraLevel("com/sun/", 3));213// Need to make sure things in jfr.jar aren't214// confused with other com/oracle/** packages215extraLevels.add(new ExtraLevel("com/oracle/jrockit", 3));216}217218219/*220* We add maximum 5 second level entries to "sun", "jdk", "java" and221* "javax" entries. Tune this parameter to get a balance on the222* cold start and footprint.223*/224private static final int MAX_PKGS_WITH_KNOWN_PREFIX = 5;225226private JarFileKind jarFileKind;227228JarMetaIndex(String fileName) throws IOException {229jar = new JarFile(fileName);230knownPrefixMap.put("sun", new HashSet<String>());231knownPrefixMap.put("jdk", new HashSet<String>());232knownPrefixMap.put("java", new HashSet<String>());233knownPrefixMap.put("javax", new HashSet<String>());234}235236/* Returns a HashSet contains the meta index string. */237HashSet<String> getMetaIndex() {238if (indexSet == null) {239synchronized(this) {240if (indexSet == null) {241indexSet = new HashSet<>();242Enumeration<JarEntry> entries = jar.entries();243boolean containsOnlyClass = true;244boolean containsOnlyResource = true;245while (entries.hasMoreElements()) {246JarEntry entry = entries.nextElement();247String name = entry.getName();248/* We only look at the non-directory entry.249MANIFEST file is also skipped. */250if (entry.isDirectory()251|| name.equals("META-INF/MANIFEST.MF")) {252continue;253}254255/* Once containsOnlyResource or containsOnlyClass256turns to false, no need to check the entry type.257*/258if (containsOnlyResource || containsOnlyClass) {259if (name.endsWith(".class")) {260containsOnlyResource = false;261} else {262containsOnlyClass = false;263}264}265266/* Add the full-qualified name of plain files under267META-INF directory to the indexSet.268*/269if (name.startsWith("META-INF")) {270indexSet.add(name);271continue;272}273274/* Add the prefix name to the knownPrefixMap if the275name starts with any string in the knownPrefix list.276*/277if (isPrefixKnown(name)) {278continue;279}280281String[] pkgElements = name.split("/");282// Last one is the class name; definitely ignoring that283if (pkgElements.length > 2) {284String meta = "";285286// Default is 2 levels of package elements287int levels = 2;288289// But for some packages we add more elements290for(ExtraLevel el : extraLevels) {291if (name.startsWith(el.prefix)) {292levels = el.levels;293break;294}295}296for (int i = 0; i < levels && i < pkgElements.length - 1; i++) {297meta += pkgElements[i] + "/";298}299300if (!meta.equals("")) {301indexSet.add(meta);302}303}304305} // end of "while" loop;306307// Add the second level package names to the indexSet for308// the predefined names such as "sun", "java" and "javax".309addKnownPrefix();310311/* Set "jarFileKind" attribute. */312if (containsOnlyClass) {313jarFileKind = JarFileKind.CLASSONLY;314} else if (containsOnlyResource) {315jarFileKind = JarFileKind.RESOURCEONLY;316} else {317jarFileKind = JarFileKind.MIXED;318}319}320}321}322return indexSet;323}324325/*326* Checks to see whether the name starts with a string which is in the predefined327* list. If it is among one of the predefined prefixes, add it to the knowPrefixMap328* and returns true, otherwise, returns false.329* Returns true if the name is in a predefined prefix list. Otherwise, returns false.330*/331boolean isPrefixKnown(String name) {332int firstSlashIndex = name.indexOf("/");333if (firstSlashIndex == -1) {334return false;335}336337String firstPkgElement = name.substring(0, firstSlashIndex);338HashSet<String> pkgSet = knownPrefixMap.get(firstPkgElement);339340/* The name does not starts with "sun", "java" or "javax". */341if (pkgSet == null) {342return false;343}344345/* Add the second level package name to the corresponding hashset. */346int secondSlashIndex = name.indexOf("/", firstSlashIndex+1);347if (secondSlashIndex == -1) {348pkgSet.add(TOP_LEVEL);349} else {350String secondPkgElement = name.substring(firstSlashIndex+1, secondSlashIndex);351pkgSet.add(secondPkgElement);352}353354return true;355}356357/*358* Adds all the second level package elements for "sun", "java" and "javax"359* if the corresponding jar file does not contain more than360* MAX_PKGS_WITH_KNOWN_PREFIX such entries.361*/362void addKnownPrefix() {363if (indexSet == null) {364return;365}366367/* Iterate through the hash map, add the second level package names368* to the indexSet if has any.369*/370for (String key : knownPrefixMap.keySet()) {371HashSet<String> pkgSetStartsWithKey = knownPrefixMap.get(key);372int setSize = pkgSetStartsWithKey.size();373374if (setSize == 0) {375continue;376}377if (setSize > JarMetaIndex.MAX_PKGS_WITH_KNOWN_PREFIX ||378pkgSetStartsWithKey.contains(TOP_LEVEL)) {379indexSet.add(key + "/");380} else {381/* If the set contains less than MAX_PKGS_WITH_KNOWN_PREFIX, add382* them to the indexSet of the MetaIndex object.383*/384for (String secondPkgElement : pkgSetStartsWithKey) {385indexSet.add(key + "/" + secondPkgElement);386}387}388} // end the outer "for"389}390391JarFileKind getJarFileKind() {392// Build meta index if it hasn't.393if (indexSet == null) {394indexSet = getMetaIndex();395}396return jarFileKind;397}398}399400401