1 /* 2 * Copyright (c) 2005, 2006, 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 sunw/util/ 75 java/ 76 sun/ 77 ... 78 </PRE> 79 * <p> A few notes about the design of the meta-index: 80 * 81 * <UL> 82 * 83 * <LI> It contains entries for multiple jar files. This is 84 * intentional, to reduce the number of disk accesses that need to be 85 * performed during startup. 86 * 87 * <LI> It is only intended to act as a fast reject mechanism to 88 * prevent application and other classes from forcing all jar files on 89 * the boot and extension class paths to be opened. It is not intended 90 * as a precise index of the contents of the jar. 91 * 92 * <LI> It should be as small as possible to reduce the amount of time 93 * required to parse it during startup. For example, adding on the 94 * secondary package element to java/ and javax/ packages 95 * ("javax/swing/", for example) causes the meta-index to grow 96 * significantly. This is why substrings of the packages have been 97 * chosen as the principal contents. 98 * 99 * <LI> It is versioned, and optional, to prevent strong dependencies 100 * between the JVM and JDK. It is also potentially applicable to more 101 * than just the boot and extension class paths. 102 * 103 * <LI> Precisely speaking, it plays different role in JVM and J2SE 104 * side. On the JVM side, meta-index file is used to speed up locating the 105 * class files only while on the J2SE side, meta-index file is used to speed 106 * up the resources file & class file. 107 * To help the JVM and J2SE code to better utilize the information in meta-index 108 * file, we mark the jar file differently. Here is the current rule we use. 109 * For jar file containing only class file, we put '!' before the jar file name; 110 * for jar file containing only resources file, we put '@' before the jar file name; 111 * for jar file containing both resources and class file, we put '#' before the 112 * jar name. 113 * Notice the fact that every jar file contains at least the manifest file, so when 114 * we say "jar file containing only class file", we don't include that file. 115 * 116 * </UL> 117 * 118 * <p> To avoid changing the behavior of the current application 119 * loader and other loaders, the current MetaIndex implementation in 120 * the JDK requires that the directory containing the meta-index be 121 * registered with the MetaIndex class before construction of the 122 * associated URLClassPath. This prevents the need for automatic 123 * searching for the meta-index in the URLClassPath code and potential 124 * changes in behavior for non-core ClassLoaders. 125 * 126 * This class depends on make/tools/MetaIndex/BuildMetaIndex.java and 127 * is used principally by sun.misc.URLClassPath. 128 */ 129 130 public class MetaIndex { 131 // Maps jar file names in registered directories to meta-indices 132 private static volatile Map<File, MetaIndex> jarMap; 133 134 // List of contents of this meta-index 135 private String[] contents; 136 137 // Indicate whether the coresponding jar file is a pure class jar file or not 138 private boolean isClassOnlyJar; 139 140 //---------------------------------------------------------------------- 141 // Registration of directories (which can cause parsing of the 142 // meta-index file if it is present), and fetching of parsed 143 // meta-indices 144 // jarMap is not strictly thread-safe when the meta index mechanism 145 // is extended for user-provided jar files in future. 146 forJar(File jar)147 public static MetaIndex forJar(File jar) { 148 return getJarMap().get(jar); 149 } 150 151 // 'synchronized' is added to protect the jarMap from being modified 152 // by multiple threads. registerDirectory(File dir)153 public static synchronized void registerDirectory(File dir) { 154 // Note that this does not currently check to see whether the 155 // directory has previously been registered, since the meta-index 156 // in a particular directory creates multiple entries in the 157 // jarMap. If this mechanism is extended beyond the boot and 158 // extension class paths (for example, automatically searching for 159 // meta-index files in directories containing jars which have been 160 // explicitly opened) then this code should be generalized. 161 // 162 // This method must be called from a privileged context. 163 File indexFile = new File(dir, "meta-index"); 164 if (indexFile.exists()) { 165 try { 166 BufferedReader reader = new BufferedReader(new FileReader(indexFile)); 167 String line = null; 168 String curJarName = null; 169 boolean isCurJarContainClassOnly = false; 170 List<String> contents = new ArrayList<String>(); 171 Map<File, MetaIndex> map = getJarMap(); 172 173 /* Convert dir into canonical form. */ 174 dir = dir.getCanonicalFile(); 175 /* Note: The first line should contain the version of 176 * the meta-index file. We have to match the right version 177 * before trying to parse this file. */ 178 line = reader.readLine(); 179 if (line == null || 180 !line.equals("% VERSION 2")) { 181 reader.close(); 182 return; 183 } 184 while ((line = reader.readLine()) != null) { 185 switch (line.charAt(0)) { 186 case '!': 187 case '#': 188 case '@': { 189 // Store away current contents, if any 190 if ((curJarName != null) && (contents.size() > 0)) { 191 map.put(new File(dir, curJarName), 192 new MetaIndex(contents, 193 isCurJarContainClassOnly)); 194 195 contents.clear(); 196 } 197 // Fetch new current jar file name 198 curJarName = line.substring(2); 199 if (line.charAt(0) == '!') { 200 isCurJarContainClassOnly = true; 201 } else if (isCurJarContainClassOnly) { 202 isCurJarContainClassOnly = false; 203 } 204 205 break; 206 } 207 case '%': 208 break; 209 default: { 210 contents.add(line); 211 } 212 } 213 } 214 // Store away current contents, if any 215 if ((curJarName != null) && (contents.size() > 0)) { 216 map.put(new File(dir, curJarName), 217 new MetaIndex(contents, isCurJarContainClassOnly)); 218 } 219 220 reader.close(); 221 222 } catch (IOException e) { 223 // Silently fail for now (similar behavior to elsewhere in 224 // extension and core loaders) 225 } 226 } 227 } 228 229 //---------------------------------------------------------------------- 230 // Public APIs 231 // 232 mayContain(String entry)233 public boolean mayContain(String entry) { 234 // Ask non-class file from class only jar returns false 235 // This check is important to avoid some class only jar 236 // files such as rt.jar are opened for resource request. 237 if (isClassOnlyJar && !entry.endsWith(".class")){ 238 return false; 239 } 240 241 String[] conts = contents; 242 for (int i = 0; i < conts.length; i++) { 243 if (entry.startsWith(conts[i])) { 244 return true; 245 } 246 } 247 return false; 248 } 249 250 251 //---------------------------------------------------------------------- 252 // Implementation only below this point 253 // @IllegalArgumentException if entries is null. MetaIndex(List<String> entries, boolean isClassOnlyJar)254 private MetaIndex(List<String> entries, boolean isClassOnlyJar) 255 throws IllegalArgumentException { 256 if (entries == null) { 257 throw new IllegalArgumentException(); 258 } 259 260 contents = entries.toArray(new String[0]); 261 this.isClassOnlyJar = isClassOnlyJar; 262 } 263 getJarMap()264 private static Map<File, MetaIndex> getJarMap() { 265 if (jarMap == null) { 266 synchronized (MetaIndex.class) { 267 if (jarMap == null) { 268 jarMap = new HashMap<File, MetaIndex>(); 269 } 270 } 271 } 272 assert jarMap != null; 273 return jarMap; 274 } 275 } 276