Path: blob/master/src/java.base/share/classes/jdk/internal/util/jar/JarIndex.java
41161 views
/*1* Copyright (c) 1999, 2019, 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 jdk.internal.util.jar;2627import sun.nio.cs.UTF_8;2829import java.io.*;30import java.util.*;31import java.util.jar.*;32import java.util.zip.*;3334import static sun.security.action.GetPropertyAction.privilegedGetProperty;3536/**37* This class is used to maintain mappings from packages, classes38* and resources to their enclosing JAR files. Mappings are kept39* at the package level except for class or resource files that40* are located at the root directory. URLClassLoader uses the mapping41* information to determine where to fetch an extension class or42* resource from.43*44* @author Zhenghua Li45* @since 1.346*/4748public class JarIndex {4950/**51* The hash map that maintains mappings from52* package/classe/resource to jar file list(s)53*/54private HashMap<String,LinkedList<String>> indexMap;5556/**57* The hash map that maintains mappings from58* jar file to package/class/resource lists59*/60private HashMap<String,LinkedList<String>> jarMap;6162/*63* An ordered list of jar file names.64*/65private String[] jarFiles;6667/**68* The index file name.69*/70public static final String INDEX_NAME = "META-INF/INDEX.LIST";7172/**73* true if, and only if, sun.misc.JarIndex.metaInfFilenames is set to true.74* If true, the names of the files in META-INF, and its subdirectories, will75* be added to the index. Otherwise, just the directory names are added.76*/77private static final boolean metaInfFilenames =78"true".equals(privilegedGetProperty("sun.misc.JarIndex.metaInfFilenames"));7980/**81* Constructs a new, empty jar index.82*/83public JarIndex() {84indexMap = new HashMap<>();85jarMap = new HashMap<>();86}8788/**89* Constructs a new index from the specified input stream.90*91* @param is the input stream containing the index data92*/93public JarIndex(InputStream is) throws IOException {94this();95read(is);96}9798/**99* Constructs a new index for the specified list of jar files.100*101* @param files the list of jar files to construct the index from.102*/103public JarIndex(String[] files) throws IOException {104this();105this.jarFiles = files;106parseJars(files);107}108109/**110* Returns the jar index, or <code>null</code> if none.111*112* @param jar the JAR file to get the index from.113* @exception IOException if an I/O error has occurred.114*/115public static JarIndex getJarIndex(JarFile jar) throws IOException {116JarIndex index = null;117JarEntry e = jar.getJarEntry(INDEX_NAME);118// if found, then load the index119if (e != null) {120index = new JarIndex(jar.getInputStream(e));121}122return index;123}124125/**126* Returns the jar files that are defined in this index.127*/128public String[] getJarFiles() {129return jarFiles;130}131132/*133* Add the key, value pair to the hashmap, the value will134* be put in a linked list which is created if necessary.135*/136private void addToList(String key, String value,137HashMap<String,LinkedList<String>> t) {138LinkedList<String> list = t.get(key);139if (list == null) {140list = new LinkedList<>();141list.add(value);142t.put(key, list);143} else if (!list.contains(value)) {144list.add(value);145}146}147148/**149* Returns the list of jar files that are mapped to the file.150*151* @param fileName the key of the mapping152*/153public LinkedList<String> get(String fileName) {154LinkedList<String> jarFiles = null;155if ((jarFiles = indexMap.get(fileName)) == null) {156/* try the package name again */157int pos;158if((pos = fileName.lastIndexOf('/')) != -1) {159jarFiles = indexMap.get(fileName.substring(0, pos));160}161}162return jarFiles;163}164165/**166* Add the mapping from the specified file to the specified167* jar file. If there were no mapping for the package of the168* specified file before, a new linked list will be created,169* the jar file is added to the list and a new mapping from170* the package to the jar file list is added to the hashmap.171* Otherwise, the jar file will be added to the end of the172* existing list.173*174* @param fileName the file name175* @param jarName the jar file that the file is mapped to176*177*/178public void add(String fileName, String jarName) {179String packageName;180int pos;181if((pos = fileName.lastIndexOf('/')) != -1) {182packageName = fileName.substring(0, pos);183} else {184packageName = fileName;185}186187addMapping(packageName, jarName);188}189190/**191* Same as add(String,String) except that it doesn't strip off from the192* last index of '/'. It just adds the jarItem (filename or package)193* as it is received.194*/195private void addMapping(String jarItem, String jarName) {196// add the mapping to indexMap197addToList(jarItem, jarName, indexMap);198199// add the mapping to jarMap200addToList(jarName, jarItem, jarMap);201}202203/**204* Go through all the jar files and construct the205* index table.206*/207private void parseJars(String[] files) throws IOException {208if (files == null) {209return;210}211212String currentJar = null;213214for (int i = 0; i < files.length; i++) {215currentJar = files[i];216ZipFile zrf = new ZipFile(currentJar.replace217('/', File.separatorChar));218219Enumeration<? extends ZipEntry> entries = zrf.entries();220while(entries.hasMoreElements()) {221ZipEntry entry = entries.nextElement();222String fileName = entry.getName();223224// Skip the META-INF directory, the index, and manifest.225// Any files in META-INF/ will be indexed explicitly226if (fileName.equals("META-INF/") ||227fileName.equals(INDEX_NAME) ||228fileName.equals(JarFile.MANIFEST_NAME) ||229fileName.startsWith("META-INF/versions/"))230continue;231232if (!metaInfFilenames || !fileName.startsWith("META-INF/")) {233add(fileName, currentJar);234} else if (!entry.isDirectory()) {235// Add files under META-INF explicitly so that certain236// services, like ServiceLoader, etc, can be located237// with greater accuracy. Directories can be skipped238// since each file will be added explicitly.239addMapping(fileName, currentJar);240}241}242243zrf.close();244}245}246247/**248* Writes the index to the specified OutputStream249*250* @param out the output stream251* @exception IOException if an I/O error has occurred252*/253public void write(OutputStream out) throws IOException {254BufferedWriter bw = new BufferedWriter255(new OutputStreamWriter(out, UTF_8.INSTANCE));256bw.write("JarIndex-Version: 1.0\n\n");257258if (jarFiles != null) {259for (int i = 0; i < jarFiles.length; i++) {260/* print out the jar file name */261String jar = jarFiles[i];262bw.write(jar + "\n");263LinkedList<String> jarlist = jarMap.get(jar);264if (jarlist != null) {265Iterator<String> listitr = jarlist.iterator();266while(listitr.hasNext()) {267bw.write(listitr.next() + "\n");268}269}270bw.write("\n");271}272bw.flush();273}274}275276277/**278* Reads the index from the specified InputStream.279*280* @param is the input stream281* @exception IOException if an I/O error has occurred282*/283public void read(InputStream is) throws IOException {284BufferedReader br = new BufferedReader285(new InputStreamReader(is, UTF_8.INSTANCE));286String line = null;287String currentJar = null;288289/* an ordered list of jar file names */290Vector<String> jars = new Vector<>();291292/* read until we see a .jar line */293while((line = br.readLine()) != null && !line.endsWith(".jar"));294295for(;line != null; line = br.readLine()) {296if (line.isEmpty())297continue;298299if (line.endsWith(".jar")) {300currentJar = line;301jars.add(currentJar);302} else {303String name = line;304addMapping(name, currentJar);305}306}307308jarFiles = jars.toArray(new String[jars.size()]);309}310311/**312* Merges the current index into another index, taking into account313* the relative path of the current index.314*315* @param toIndex The destination index which the current index will316* merge into.317* @param path The relative path of the this index to the destination318* index.319*320*/321public void merge(JarIndex toIndex, String path) {322Iterator<Map.Entry<String,LinkedList<String>>> itr = indexMap.entrySet().iterator();323while(itr.hasNext()) {324Map.Entry<String,LinkedList<String>> e = itr.next();325String packageName = e.getKey();326LinkedList<String> from_list = e.getValue();327Iterator<String> listItr = from_list.iterator();328while(listItr.hasNext()) {329String jarName = listItr.next();330if (path != null) {331jarName = path.concat(jarName);332}333toIndex.addMapping(packageName, jarName);334}335}336}337}338339340