1 /* 2 * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.misc; 27 28 import java.io.BufferedReader; 29 import java.io.FileReader; 30 import java.io.File; 31 import java.io.IOException; 32 import java.util.ArrayList; 33 import java.util.Collections; 34 import java.util.HashMap; 35 import java.util.List; 36 import java.util.Map; 37 38 /* 39 * MetaIndex is intended to decrease startup time (in particular cold 40 * start, when files are not yet in the disk cache) by providing a 41 * quick reject mechanism for probes into jar files. The on-disk 42 * representation of the meta-index is a flat text file with per-jar 43 * entries indicating (generally speaking) prefixes of package names 44 * contained in the jar. As an example, here is an edited excerpt of 45 * the meta-index generated for jre/lib in the current build: 46 * 47 <PRE> 48 % VERSION 1 49 # charsets.jar 50 sun/ 51 # jce.jar 52 javax/ 53 ! jsse.jar 54 sun/ 55 com/sun/net/ 56 javax/ 57 com/sun/security/ 58 @ resources.jar 59 com/sun/xml/ 60 com/sun/rowset/ 61 com/sun/org/ 62 sun/ 63 com/sun/imageio/ 64 javax/ 65 com/sun/java/swing/ 66 META-INF/services/ 67 com/sun/java/util/jar/pack/ 68 com/sun/corba/ 69 com/sun/jndi/ 70 ! rt.jar 71 org/w3c/ 72 com/sun/imageio/ 73 javax/ 74 java/ 75 sun/ 76 ... 77 </PRE> 78 * <p> A few notes about the design of the meta-index: 79 * 80 * <UL> 81 * 82 * <LI> It contains entries for multiple jar files. This is 83 * intentional, to reduce the number of disk accesses that need to be 84 * performed during startup. 85 * 86 * <LI> It is only intended to act as a fast reject mechanism to 87 * prevent application and other classes from forcing all jar files on 88 * the boot and extension class paths to be opened. It is not intended 89 * as a precise index of the contents of the jar. 90 * 91 * <LI> It should be as small as possible to reduce the amount of time 92 * required to parse it during startup. For example, adding on the 93 * secondary package element to java/ and javax/ packages 94 * ("javax/swing/", for example) causes the meta-index to grow 95 * significantly. This is why substrings of the packages have been 96 * chosen as the principal contents. 97 * 98 * <LI> It is versioned, and optional, to prevent strong dependencies 99 * between the JVM and JDK. It is also potentially applicable to more 100 * than just the boot and extension class paths. 101 * 102 * <LI> Precisely speaking, it plays different role in JVM and J2SE 103 * side. On the JVM side, meta-index file is used to speed up locating the 104 * class files only while on the J2SE side, meta-index file is used to speed 105 * up the resources file & class file. 106 * To help the JVM and J2SE code to better utilize the information in meta-index 107 * file, we mark the jar file differently. Here is the current rule we use. 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 111 * jar name. 112 * Notice the fact that every jar file contains at least the manifest file, so when 113 * we say "jar file containing only class file", we don't include that file. 114 * 115 * </UL> 116 * 117 * <p> To avoid changing the behavior of the current application 118 * loader and other loaders, the current MetaIndex implementation in 119 * the JDK requires that the directory containing the meta-index be 120 * registered with the MetaIndex class before construction of the 121 * associated URLClassPath. This prevents the need for automatic 122 * searching for the meta-index in the URLClassPath code and potential 123 * changes in behavior for non-core ClassLoaders. 124 * 125 * This class depends on make/tools/MetaIndex/BuildMetaIndex.java and 126 * is used principally by sun.misc.URLClassPath. 127 */ 128 129 public class MetaIndex { 130 // Maps jar file names in registered directories to meta-indices 131 private static volatile Map<File, MetaIndex> jarMap; 132 133 // List of contents of this meta-index 134 private String[] contents; 135 136 // Indicate whether the coresponding jar file is a pure class jar file or not 137 private boolean isClassOnlyJar; 138 139 //---------------------------------------------------------------------- 140 // Registration of directories (which can cause parsing of the 141 // meta-index file if it is present), and fetching of parsed 142 // meta-indices 143 // jarMap is not strictly thread-safe when the meta index mechanism 144 // is extended for user-provided jar files in future. 145 forJar(File jar)146 public static MetaIndex forJar(File jar) { 147 return getJarMap().get(jar); 148 } 149 150 // 'synchronized' is added to protect the jarMap from being modified 151 // by multiple threads. registerDirectory(File dir)152 public static synchronized void registerDirectory(File dir) { 153 // Note that this does not currently check to see whether the 154 // directory has previously been registered, since the meta-index 155 // in a particular directory creates multiple entries in the 156 // jarMap. If this mechanism is extended beyond the boot and 157 // extension class paths (for example, automatically searching for 158 // meta-index files in directories containing jars which have been 159 // explicitly opened) then this code should be generalized. 160 // 161 // This method must be called from a privileged context. 162 File indexFile = new File(dir, "meta-index"); 163 if (indexFile.exists()) { 164 try { 165 BufferedReader reader = new BufferedReader(new FileReader(indexFile)); 166 String line = null; 167 String curJarName = null; 168 boolean isCurJarContainClassOnly = false; 169 List<String> contents = new ArrayList<String>(); 170 Map<File, MetaIndex> map = getJarMap(); 171 172 /* Convert dir into canonical form. */ 173 dir = dir.getCanonicalFile(); 174 /* Note: The first line should contain the version of 175 * the meta-index file. We have to match the right version 176 * before trying to parse this file. */ 177 line = reader.readLine(); 178 if (line == null || 179 !line.equals("% VERSION 2")) { 180 reader.close(); 181 return; 182 } 183 while ((line = reader.readLine()) != null) { 184 switch (line.charAt(0)) { 185 case '!': 186 case '#': 187 case '@': { 188 // Store away current contents, if any 189 if ((curJarName != null) && (contents.size() > 0)) { 190 map.put(new File(dir, curJarName), 191 new MetaIndex(contents, 192 isCurJarContainClassOnly)); 193 194 contents.clear(); 195 } 196 // Fetch new current jar file name 197 curJarName = line.substring(2); 198 if (line.charAt(0) == '!') { 199 isCurJarContainClassOnly = true; 200 } else if (isCurJarContainClassOnly) { 201 isCurJarContainClassOnly = false; 202 } 203 204 break; 205 } 206 case '%': 207 break; 208 default: { 209 contents.add(line); 210 } 211 } 212 } 213 // Store away current contents, if any 214 if ((curJarName != null) && (contents.size() > 0)) { 215 map.put(new File(dir, curJarName), 216 new MetaIndex(contents, isCurJarContainClassOnly)); 217 } 218 219 reader.close(); 220 221 } catch (IOException e) { 222 // Silently fail for now (similar behavior to elsewhere in 223 // extension and core loaders) 224 } 225 } 226 } 227 228 //---------------------------------------------------------------------- 229 // Public APIs 230 // 231 mayContain(String entry)232 public boolean mayContain(String entry) { 233 // Ask non-class file from class only jar returns false 234 // This check is important to avoid some class only jar 235 // files such as rt.jar are opened for resource request. 236 if (isClassOnlyJar && !entry.endsWith(".class")){ 237 return false; 238 } 239 240 String[] conts = contents; 241 for (int i = 0; i < conts.length; i++) { 242 if (entry.startsWith(conts[i])) { 243 return true; 244 } 245 } 246 return false; 247 } 248 249 250 //---------------------------------------------------------------------- 251 // Implementation only below this point 252 // @IllegalArgumentException if entries is null. MetaIndex(List<String> entries, boolean isClassOnlyJar)253 private MetaIndex(List<String> entries, boolean isClassOnlyJar) 254 throws IllegalArgumentException { 255 if (entries == null) { 256 throw new IllegalArgumentException(); 257 } 258 259 contents = entries.toArray(new String[0]); 260 this.isClassOnlyJar = isClassOnlyJar; 261 } 262 getJarMap()263 private static Map<File, MetaIndex> getJarMap() { 264 if (jarMap == null) { 265 synchronized (MetaIndex.class) { 266 if (jarMap == null) { 267 jarMap = new HashMap<File, MetaIndex>(); 268 } 269 } 270 } 271 assert jarMap != null; 272 return jarMap; 273 } 274 } 275