8302819: Remove JAR Index

Reviewed-by: mchung, alanb, lancea, jpai
This commit is contained in:
Eirik Bjorsnos 2023-04-10 11:23:21 +00:00 committed by Lance Andersen
parent 0243da2e4a
commit 0d45a524b3
24 changed files with 45 additions and 1394 deletions

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -207,12 +207,18 @@ public class JarFile extends ZipFile {
*/
public static final String MANIFEST_NAME = META_INF + "MANIFEST.MF";
/**
* The 'JAR index' feature has been removed, but JarInputStream and
* JarVerifier's verification of signed jars still need to be
* able to skip this entry.
*/
static final String INDEX_NAME = "META-INF/INDEX.LIST";
/**
* Returns the version that represents the unversioned configuration of a
* multi-release jar file.
*
* @return the version that represents the unversioned configuration
*
* @since 9
*/
public static Runtime.Version baseVersion() {

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -28,7 +28,6 @@ package java.util.jar;
import java.util.zip.*;
import java.io.*;
import sun.security.util.ManifestEntryVerifier;
import jdk.internal.util.jar.JarIndex;
/**
* The {@code JarInputStream} class, which extends {@link ZipInputStream},
@ -185,7 +184,7 @@ public class JarInputStream extends ZipInputStream {
}
} else {
e = first;
if (first.getName().equalsIgnoreCase(JarIndex.INDEX_NAME))
if (first.getName().equalsIgnoreCase(JarFile.INDEX_NAME))
tryManifest = true;
first = null;
}

View file

@ -33,7 +33,6 @@ import java.security.SignatureException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import jdk.internal.util.jar.JarIndex;
import sun.security.util.ManifestDigester;
import sun.security.util.ManifestEntryVerifier;
import sun.security.util.SignatureFileVerifier;
@ -146,7 +145,7 @@ class JarVerifier {
}
String uname = name.toUpperCase(Locale.ENGLISH);
if (uname.equals(JarFile.MANIFEST_NAME) ||
uname.equals(JarIndex.INDEX_NAME)) {
uname.equals(JarFile.INDEX_NAME)) {
return;
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -71,8 +71,6 @@ import java.util.zip.ZipFile;
import jdk.internal.access.JavaNetURLAccess;
import jdk.internal.access.JavaUtilZipFileAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.util.jar.InvalidJarIndexError;
import jdk.internal.util.jar.JarIndex;
import sun.net.util.URLUtil;
import sun.net.www.ParseUtil;
import sun.security.action.GetPropertyAction;
@ -91,7 +89,6 @@ public class URLClassPath {
private static final boolean DISABLE_ACC_CHECKING;
private static final boolean DISABLE_CP_URL_CHECK;
private static final boolean DEBUG_CP_URL_CHECK;
private static final boolean ENABLE_JAR_INDEX;
static {
Properties props = GetPropertyAction.privilegedGetProperties();
@ -111,9 +108,6 @@ public class URLClassPath {
// the check is not disabled).
p = props.getProperty("jdk.net.URLClassPath.showIgnoredClassPathEntries");
DEBUG_CP_URL_CHECK = p != null ? p.equals("true") || p.isEmpty() : false;
p = props.getProperty("jdk.net.URLClassPath.enableJarIndex");
ENABLE_JAR_INDEX = p != null ? p.equals("true") || p.isEmpty() : false;
}
/* The original search path of URLs. */
@ -714,7 +708,6 @@ public class URLClassPath {
private static class JarLoader extends Loader {
private JarFile jar;
private final URL csu;
private JarIndex index;
private URLStreamHandler handler;
private final HashMap<String, Loader> lmap;
@SuppressWarnings("removal")
@ -779,31 +772,6 @@ public class URLClassPath {
Thread.dumpStack();
}
jar = getJarFile(csu);
if (!ENABLE_JAR_INDEX) {
return null;
}
index = JarIndex.getJarIndex(jar);
if (index != null) {
String[] jarfiles = index.getJarFiles();
// Add all the dependent URLs to the lmap so that loaders
// will not be created for them by URLClassPath.getLoader(int)
// if the same URL occurs later on the main class path. We set
// Loader to null here to avoid creating a Loader for each
// URL until we actually need to try to load something from them.
for (int i = 0; i < jarfiles.length; i++) {
try {
@SuppressWarnings("deprecation")
URL jarURL = new URL(csu, jarfiles[i]);
// If a non-null loader already exists, leave it alone.
String urlNoFragString = URLUtil.urlNoFragString(jarURL);
if (!lmap.containsKey(urlNoFragString)) {
lmap.put(urlNoFragString, null);
}
} catch (MalformedURLException e) {
continue;
}
}
}
return null;
}
}, acc);
@ -847,18 +815,6 @@ public class URLClassPath {
return checkJar(jarFile);
}
/*
* Returns the index of this JarLoader if it exists.
*/
JarIndex getIndex() {
try {
ensureOpen();
} catch (IOException e) {
throw new InternalError(e);
}
return index;
}
/*
* Creates the resource and if the check flag is set to true, checks if
* is its okay to return the resource.
@ -968,138 +924,15 @@ public class URLClassPath {
if (entry != null)
return checkResource(name, check, entry);
if (index == null)
return null;
HashSet<String> visited = new HashSet<>();
return getResource(name, check, visited);
}
/*
* Version of getResource() that tracks the jar files that have been
* visited by linking through the index files. This helper method uses
* a HashSet to store the URLs of jar files that have been searched and
* uses it to avoid going into an infinite loop, looking for a
* non-existent resource.
*/
@SuppressWarnings("removal")
Resource getResource(final String name, boolean check,
Set<String> visited) {
Resource res;
String[] jarFiles;
int count = 0;
List<String> jarFilesList;
/* If there no jar files in the index that can potential contain
* this resource then return immediately.
*/
if ((jarFilesList = index.get(name)) == null)
return null;
do {
int size = jarFilesList.size();
jarFiles = jarFilesList.toArray(new String[size]);
/* loop through the mapped jar file list */
while (count < size) {
String jarName = jarFiles[count++];
JarLoader newLoader;
final URL url;
try{
@SuppressWarnings("deprecation")
var _unused = url = new URL(csu, jarName);
String urlNoFragString = URLUtil.urlNoFragString(url);
if ((newLoader = (JarLoader)lmap.get(urlNoFragString)) == null) {
/* no loader has been set up for this jar file
* before
*/
newLoader = AccessController.doPrivileged(
new PrivilegedExceptionAction<>() {
public JarLoader run() throws IOException {
return new JarLoader(url, handler,
lmap, acc);
}
}, acc);
/* this newly opened jar file has its own index,
* merge it into the parent's index, taking into
* account the relative path.
*/
JarIndex newIndex = newLoader.getIndex();
if (newIndex != null) {
int pos = jarName.lastIndexOf('/');
newIndex.merge(this.index, (pos == -1 ?
null : jarName.substring(0, pos + 1)));
}
/* put it in the global hashtable */
lmap.put(urlNoFragString, newLoader);
}
} catch (PrivilegedActionException | MalformedURLException e) {
continue;
}
/* Note that the addition of the url to the list of visited
* jars incorporates a check for presence in the hashmap
*/
boolean visitedURL = !visited.add(URLUtil.urlNoFragString(url));
if (!visitedURL) {
try {
newLoader.ensureOpen();
} catch (IOException e) {
throw new InternalError(e);
}
final JarEntry entry = newLoader.jar.getJarEntry(name);
if (entry != null) {
return newLoader.checkResource(name, check, entry);
}
/* Verify that at least one other resource with the
* same package name as the lookedup resource is
* present in the new jar
*/
if (!newLoader.validIndex(name)) {
/* the mapping is wrong */
throw new InvalidJarIndexError("Invalid index");
}
}
/* If newLoader is the current loader or if it is a
* loader that has already been searched or if the new
* loader does not have an index then skip it
* and move on to the next loader.
*/
if (visitedURL || newLoader == this ||
newLoader.getIndex() == null) {
continue;
}
/* Process the index of the new loader
*/
if ((res = newLoader.getResource(name, check, visited))
!= null) {
return res;
}
}
// Get the list of jar files again as the list could have grown
// due to merging of index files.
jarFilesList = index.get(name);
// If the count is unchanged, we are done.
} while (count < jarFilesList.size());
return null;
}
/*
* Returns the JAR file local class path, or null if none.
*/
@Override
URL[] getClassPath() throws IOException {
if (index != null) {
return null;
}
ensureOpen();
// Only get manifest when necessary

View file

@ -1,55 +0,0 @@
/*
* Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.util.jar;
/**
* Thrown if the URLClassLoader finds the INDEX.LIST file of
* a jar file contains incorrect information.
*
* @since 9
*/
public class InvalidJarIndexError extends Error {
@java.io.Serial
static final long serialVersionUID = 0L;
/**
* Constructs an {@code InvalidJarIndexError} with no detail message.
*/
public InvalidJarIndexError() {
super();
}
/**
* Constructs an {@code InvalidJarIndexError} with the specified detail message.
*
* @param s the detail message.
*/
public InvalidJarIndexError(String s) {
super(s);
}
}

View file

@ -1,333 +0,0 @@
/*
* Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.util.jar;
import sun.nio.cs.UTF_8;
import java.io.*;
import java.util.*;
import java.util.jar.*;
import java.util.zip.*;
import static sun.security.action.GetPropertyAction.privilegedGetProperty;
/**
* This class is used to maintain mappings from packages, classes
* and resources to their enclosing JAR files. Mappings are kept
* at the package level except for class or resource files that
* are located at the root directory. URLClassLoader uses the mapping
* information to determine where to fetch an extension class or
* resource from.
*
* @author Zhenghua Li
* @since 1.3
*/
public class JarIndex {
/**
* The hash map that maintains mappings from
* package/class/resource to jar file list(s)
*/
private final HashMap<String, List<String>> indexMap;
/**
* The hash map that maintains mappings from
* jar file to package/class/resource lists
*/
private final HashMap<String, List<String>> jarMap;
/*
* An ordered list of jar file names.
*/
private String[] jarFiles;
/**
* The index file name.
*/
public static final String INDEX_NAME = "META-INF/INDEX.LIST";
/**
* true if, and only if, sun.misc.JarIndex.metaInfFilenames is set to true.
* If true, the names of the files in META-INF, and its subdirectories, will
* be added to the index. Otherwise, just the directory names are added.
*/
private static final boolean metaInfFilenames =
"true".equals(privilegedGetProperty("sun.misc.JarIndex.metaInfFilenames"));
/**
* Constructs a new, empty jar index.
*/
public JarIndex() {
indexMap = new HashMap<>();
jarMap = new HashMap<>();
}
/**
* Constructs a new index from the specified input stream.
*
* @param is the input stream containing the index data
*/
public JarIndex(InputStream is) throws IOException {
this();
read(is);
}
/**
* Constructs a new index for the specified list of jar files.
*
* @param files the list of jar files to construct the index from.
*/
public JarIndex(String[] files) throws IOException {
this();
this.jarFiles = files;
parseJars(files);
}
/**
* Returns the jar index, or <code>null</code> if none.
*
* @param jar the JAR file to get the index from.
* @exception IOException if an I/O error has occurred.
*/
public static JarIndex getJarIndex(JarFile jar) throws IOException {
JarIndex index = null;
JarEntry e = jar.getJarEntry(INDEX_NAME);
// if found, then load the index
if (e != null) {
index = new JarIndex(jar.getInputStream(e));
}
return index;
}
/**
* Returns the jar files that are defined in this index.
*/
public String[] getJarFiles() {
return jarFiles;
}
/*
* Add the key, value pair to the hashmap, the value will
* be put in a list which is created if necessary.
*/
private void addToList(String key, String value,
HashMap<String, List<String>> t) {
List<String> list = t.get(key);
if (list == null) {
list = new ArrayList<>(1);
list.add(value);
t.put(key, list);
} else if (!list.contains(value)) {
list.add(value);
}
}
/**
* Returns the list of jar files that are mapped to the file.
*
* @param fileName the key of the mapping
*/
public List<String> get(String fileName) {
List<String> jarFiles;
if ((jarFiles = indexMap.get(fileName)) == null) {
/* try the package name again */
int pos;
if((pos = fileName.lastIndexOf('/')) != -1) {
jarFiles = indexMap.get(fileName.substring(0, pos));
}
}
return jarFiles;
}
/**
* Add the mapping from the specified file to the specified
* jar file. If there were no mapping for the package of the
* specified file before, a new list will be created,
* the jar file is added to the list and a new mapping from
* the package to the jar file list is added to the hashmap.
* Otherwise, the jar file will be added to the end of the
* existing list.
*
* @param fileName the file name
* @param jarName the jar file that the file is mapped to
*
*/
public void add(String fileName, String jarName) {
String packageName;
int pos;
if((pos = fileName.lastIndexOf('/')) != -1) {
packageName = fileName.substring(0, pos);
} else {
packageName = fileName;
}
addMapping(packageName, jarName);
}
/**
* Same as add(String,String) except that it doesn't strip off from the
* last index of '/'. It just adds the jarItem (filename or package)
* as it is received.
*/
private void addMapping(String jarItem, String jarName) {
// add the mapping to indexMap
addToList(jarItem, jarName, indexMap);
// add the mapping to jarMap
addToList(jarName, jarItem, jarMap);
}
/**
* Go through all the jar files and construct the
* index table.
*/
private void parseJars(String[] files) throws IOException {
if (files == null) {
return;
}
String currentJar = null;
for (int i = 0; i < files.length; i++) {
currentJar = files[i];
ZipFile zrf = new ZipFile(currentJar.replace
('/', File.separatorChar));
Enumeration<? extends ZipEntry> entries = zrf.entries();
while(entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
String fileName = entry.getName();
// Skip the META-INF directory, the index, and manifest.
// Any files in META-INF/ will be indexed explicitly
if (fileName.equals("META-INF/") ||
fileName.equals(INDEX_NAME) ||
fileName.equals(JarFile.MANIFEST_NAME) ||
fileName.startsWith("META-INF/versions/"))
continue;
if (!metaInfFilenames || !fileName.startsWith("META-INF/")) {
add(fileName, currentJar);
} else if (!entry.isDirectory()) {
// Add files under META-INF explicitly so that certain
// services, like ServiceLoader, etc, can be located
// with greater accuracy. Directories can be skipped
// since each file will be added explicitly.
addMapping(fileName, currentJar);
}
}
zrf.close();
}
}
/**
* Writes the index to the specified OutputStream
*
* @param out the output stream
* @exception IOException if an I/O error has occurred
*/
public void write(OutputStream out) throws IOException {
BufferedWriter bw = new BufferedWriter
(new OutputStreamWriter(out, UTF_8.INSTANCE));
bw.write("JarIndex-Version: 1.0\n\n");
if (jarFiles != null) {
for (int i = 0; i < jarFiles.length; i++) {
/* print out the jar file name */
String jar = jarFiles[i];
bw.write(jar + "\n");
List<String> jarlist = jarMap.get(jar);
if (jarlist != null) {
for (String s : jarlist) {
bw.write(s + "\n");
}
}
bw.write("\n");
}
bw.flush();
}
}
/**
* Reads the index from the specified InputStream.
*
* @param is the input stream
* @exception IOException if an I/O error has occurred
*/
public void read(InputStream is) throws IOException {
BufferedReader br = new BufferedReader
(new InputStreamReader(is, UTF_8.INSTANCE));
String line;
String currentJar = null;
/* an ordered list of jar file names */
ArrayList<String> jars = new ArrayList<>();
/* read until we see a .jar line */
while((line = br.readLine()) != null && !line.endsWith(".jar"));
for(;line != null; line = br.readLine()) {
if (line.isEmpty())
continue;
if (line.endsWith(".jar")) {
currentJar = line;
jars.add(currentJar);
} else {
String name = line;
addMapping(name, currentJar);
}
}
jarFiles = jars.toArray(new String[jars.size()]);
}
/**
* Merges the current index into another index, taking into account
* the relative path of the current index.
*
* @param toIndex The destination index which the current index will
* merge into.
* @param path The relative path of the this index to the destination
* index.
*
*/
public void merge(JarIndex toIndex, String path) {
for (Map.Entry<String, List<String>> e : indexMap.entrySet()) {
String packageName = e.getKey();
List<String> from_list = e.getValue();
for (String jarName : from_list) {
if (path != null) {
jarName = path.concat(jarName);
}
toIndex.addMapping(packageName, jarName);
}
}
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -274,8 +274,6 @@ module java.base {
jdk.unsupported;
exports jdk.internal.vm.vector to
jdk.incubator.vector;
exports jdk.internal.util.jar to
jdk.jartool;
exports jdk.internal.util.xml to
jdk.jfr;
exports jdk.internal.util.xml.impl to