src/java.base/share/classes/java/util/jar/JarFile.java
Print this page
*** 24,33 ****
--- 24,34 ----
*/
package java.util.jar;
import jdk.internal.misc.SharedSecrets;
+ import jdk.internal.misc.JavaUtilZipFileAccess;
import sun.security.action.GetPropertyAction;
import sun.security.util.ManifestEntryVerifier;
import sun.security.util.SignatureFileVerifier;
import java.io.ByteArrayInputStream;
*** 43,56 ****
--- 44,59 ----
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
+ import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Spliterator;
import java.util.Spliterators;
+ import java.util.stream.Collector;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
*** 161,173 ****
--- 164,180 ----
// indicates if Class-Path attribute present
private boolean hasClassPathAttribute;
// true if manifest checked for special attributes
private volatile boolean hasCheckedSpecialAttributes;
+ private static final JavaUtilZipFileAccess JUZFA;
+
static {
// Set up JavaUtilJarAccess in SharedSecrets
SharedSecrets.setJavaUtilJarAccess(new JavaUtilJarAccessImpl());
+ // Get JavaUtilZipFileAccess from SharedSecrets
+ JUZFA = jdk.internal.misc.SharedSecrets.getJavaUtilZipFileAccess();
// multi-release jar file versions >= 9
BASE_VERSION = Runtime.Version.parse(Integer.toString(8));
BASE_VERSION_MAJOR = BASE_VERSION.major();
String jarVersion = GetPropertyAction.privilegedGetProperty("jdk.util.jar.version");
int runtimeVersion = Runtime.version().major();
*** 422,433 ****
}
return man;
}
private String[] getMetaInfEntryNames() {
! return jdk.internal.misc.SharedSecrets.getJavaUtilZipFileAccess()
! .getMetaInfEntryNames((ZipFile)this);
}
/**
* Returns the {@code JarEntry} for the given base entry name or
* {@code null} if not found.
--- 429,439 ----
}
return man;
}
private String[] getMetaInfEntryNames() {
! return JUZFA.getMetaInfEntryNames((ZipFile)this);
}
/**
* Returns the {@code JarEntry} for the given base entry name or
* {@code null} if not found.
*** 495,556 ****
* If a subclass overrides this method, assure that the override method
* invokes {@code super.getEntry(name)} to obtain all versioned entries.
* </div>
*/
public ZipEntry getEntry(String name) {
! ZipEntry ze = super.getEntry(name);
! if (ze != null) {
! return new JarFileEntry(ze);
! }
! // no matching base entry, but maybe there is a versioned entry,
! // like a new private class
if (isMultiRelease()) {
! ze = new ZipEntry(name);
! ZipEntry vze = getVersionedEntry(ze);
! if (ze != vze) {
! return new JarFileEntry(name, vze);
! }
! }
! return null;
! }
!
! private class JarEntryIterator implements Enumeration<JarEntry>,
! Iterator<JarEntry>
! {
! final Enumeration<? extends ZipEntry> e = JarFile.super.entries();
!
! public boolean hasNext() {
! return e.hasMoreElements();
! }
!
! public JarEntry next() {
! ZipEntry ze = e.nextElement();
! return new JarFileEntry(ze.getName(), ze);
! }
!
! public boolean hasMoreElements() {
! return hasNext();
! }
!
! public JarEntry nextElement() {
! return next();
! }
!
! public Iterator<JarEntry> asIterator() {
! return this;
}
}
/**
* Returns an enumeration of the jar file entries.
*
* @return an enumeration of the jar file entries
* @throws IllegalStateException
* may be thrown if the jar file has been closed
*/
public Enumeration<JarEntry> entries() {
! return new JarEntryIterator();
}
/**
* Returns an ordered {@code Stream} over the jar file entries.
* Entries appear in the {@code Stream} in the order they appear in
--- 501,526 ----
* If a subclass overrides this method, assure that the override method
* invokes {@code super.getEntry(name)} to obtain all versioned entries.
* </div>
*/
public ZipEntry getEntry(String name) {
! JarFileEntry je = getEntry0(name);
if (isMultiRelease()) {
! return getVersionedEntry(name, je);
}
+ return je;
}
/**
* Returns an enumeration of the jar file entries.
*
* @return an enumeration of the jar file entries
* @throws IllegalStateException
* may be thrown if the jar file has been closed
*/
public Enumeration<JarEntry> entries() {
! return JUZFA.entries(this, JarFileEntry::new);
}
/**
* Returns an ordered {@code Stream} over the jar file entries.
* Entries appear in the {@code Stream} in the order they appear in
*** 559,638 ****
* @return an ordered {@code Stream} of entries in this jar file
* @throws IllegalStateException if the jar file has been closed
* @since 1.8
*/
public Stream<JarEntry> stream() {
! return StreamSupport.stream(Spliterators.spliterator(
! new JarEntryIterator(), size(),
! Spliterator.ORDERED | Spliterator.DISTINCT |
! Spliterator.IMMUTABLE | Spliterator.NONNULL), false);
! }
!
! private ZipEntry searchForVersionedEntry(final int version, String name) {
! ZipEntry vze = null;
! String sname = "/" + name;
! int i = version;
! while (i > BASE_VERSION_MAJOR) {
! vze = super.getEntry(META_INF_VERSIONS + i + sname);
! if (vze != null) break;
! i--;
}
! return vze;
}
! private ZipEntry getVersionedEntry(ZipEntry ze) {
! ZipEntry vze = null;
if (BASE_VERSION_MAJOR < versionMajor) {
- String name = ze.getName();
if (!name.startsWith(META_INF)) {
! vze = searchForVersionedEntry(versionMajor, name);
}
}
! return vze == null ? ze : vze;
}
! /**
! * Returns the real name of a {@code JarEntry}. If this {@code JarFile} is
! * a multi-release jar file and is configured to be processed as such, the
! * name returned by this method is the path name of the versioned entry
! * that the {@code JarEntry} represents, rather than the path name of the
! * base entry that {@link JarEntry#getName()} returns. If the
! * {@code JarEntry} does not represent a versioned entry, or the
! * jar file is not a multi-release jar file or {@code JarFile} is not
! * configured for processing a multi-release jar file, this method returns
! * the same name that {@link JarEntry#getName()} returns.
! *
! * @param entry the JarEntry
! * @return the real name of the JarEntry
! * @since 9
! */
String getRealName(JarEntry entry) {
! if (entry instanceof JarFileEntry) {
! return ((JarFileEntry)entry).realName();
! }
! return entry.getName();
}
private class JarFileEntry extends JarEntry {
! final private String name;
! JarFileEntry(ZipEntry ze) {
! super(isMultiRelease() ? getVersionedEntry(ze) : ze);
! this.name = ze.getName();
}
JarFileEntry(String name, ZipEntry vze) {
super(vze);
! this.name = name;
}
public Attributes getAttributes() throws IOException {
Manifest man = JarFile.this.getManifest();
if (man != null) {
return man.getAttributes(super.getName());
} else {
return null;
}
}
public Certificate[] getCertificates() {
try {
maybeInstantiateVerifier();
} catch (IOException e) {
throw new RuntimeException(e);
--- 529,642 ----
* @return an ordered {@code Stream} of entries in this jar file
* @throws IllegalStateException if the jar file has been closed
* @since 1.8
*/
public Stream<JarEntry> stream() {
! return JUZFA.stream(this, JarFileEntry::new);
! }
!
! /**
! * Returns a {@code Stream} of the versioned jar file entries.
! *
! * <p>If this {@code JarFile} is a multi-release jar file and is configured to
! * be processed as such, then an entry in the stream is the latest versioned entry
! * associated with the corresponding base entry name. The maximum version of the
! * latest versioned entry is the version returned by {@link #getVersion()}.
! * The returned stream may include an entry that only exists as a versioned entry.
! *
! * If the jar file is not a multi-release jar file or the {@code JarFile} is not
! * configured for processing a multi-release jar file, this method returns the
! * same stream that {@link #stream()} returns.
! *
! * @return stream of versioned entries
! * @since 10
! */
! public Stream<JarEntry> versionedStream() {
!
! if (isMultiRelease()) {
! return JUZFA.entryNameStream(this).map(this::getBasename)
! .filter(Objects::nonNull)
! .distinct()
! .map(this::getJarEntry);
}
! return stream();
}
! /*
! * Invokes {@ZipFile}'s getEntry to Return a {@code JarFileEntry} for the
! * given entry name or {@code null} if not found.
! */
! private JarFileEntry getEntry0(String name) {
! return (JarFileEntry)JUZFA.getEntry(this, name, JarFileEntry::new);
! }
!
! private String getBasename(String name) {
! if (name.startsWith(META_INF_VERSIONS)) {
! int off = META_INF_VERSIONS.length();
! int index = name.indexOf('/', off);
! try {
! // filter out dir META-INF/versions/ and META-INF/versions/*/
! // and any entry with version > 'version'
! if (index == -1 || index == (name.length() - 1) ||
! Integer.parseInt(name, off, index, 10) > versionMajor) {
! return null;
! }
! } catch (NumberFormatException x) {
! return null; // remove malformed entries silently
! }
! // map to its base name
! return name.substring(index + 1);
! }
! return name;
! }
!
! private JarEntry getVersionedEntry(String name, JarEntry je) {
if (BASE_VERSION_MAJOR < versionMajor) {
if (!name.startsWith(META_INF)) {
! // search for versioned entry
! int v = versionMajor;
! while (v > BASE_VERSION_MAJOR) {
! JarFileEntry vje = getEntry0(META_INF_VERSIONS + v + "/" + name);
! if (vje != null) {
! return vje.withBasename(name);
! }
! v--;
}
}
! }
! return je;
}
! // placeholder for now
String getRealName(JarEntry entry) {
! return entry.getRealName();
}
private class JarFileEntry extends JarEntry {
! private String basename;
! JarFileEntry(String name) {
! super(name);
! this.basename = name;
}
+
JarFileEntry(String name, ZipEntry vze) {
super(vze);
! this.basename = name;
}
+
+ @Override
public Attributes getAttributes() throws IOException {
Manifest man = JarFile.this.getManifest();
if (man != null) {
return man.getAttributes(super.getName());
} else {
return null;
}
}
+
+ @Override
public Certificate[] getCertificates() {
try {
maybeInstantiateVerifier();
} catch (IOException e) {
throw new RuntimeException(e);
*** 640,649 ****
--- 644,655 ----
if (certs == null && jv != null) {
certs = jv.getCerts(JarFile.this, realEntry());
}
return certs == null ? null : certs.clone();
}
+
+ @Override
public CodeSigner[] getCodeSigners() {
try {
maybeInstantiateVerifier();
} catch (IOException e) {
throw new RuntimeException(e);
*** 651,674 ****
if (signers == null && jv != null) {
signers = jv.getCodeSigners(JarFile.this, realEntry());
}
return signers == null ? null : signers.clone();
}
JarFileEntry realEntry() {
if (isMultiRelease() && versionMajor != BASE_VERSION_MAJOR) {
String entryName = super.getName();
! return entryName.equals(this.name) ? this : new JarFileEntry(entryName, this);
}
return this;
}
- String realName() {
- return super.getName();
- }
! @Override
! public String getName() {
! return name;
}
}
/*
* Ensures that the JarVerifier has been created if one is
--- 657,690 ----
if (signers == null && jv != null) {
signers = jv.getCodeSigners(JarFile.this, realEntry());
}
return signers == null ? null : signers.clone();
}
+
+ @Override
+ public String getRealName() {
+ return super.getName();
+ }
+
+ @Override
+ public String getName() {
+ return basename;
+ }
+
JarFileEntry realEntry() {
if (isMultiRelease() && versionMajor != BASE_VERSION_MAJOR) {
String entryName = super.getName();
! return entryName == basename || entryName.equals(basename) ?
! this : new JarFileEntry(entryName, this);
}
return this;
}
! // changes the basename, returns "this"
! JarFileEntry withBasename(String name) {
! basename = name;
! return this;
}
}
/*
* Ensures that the JarVerifier has been created if one is
*** 702,712 ****
// verifier
verify = false;
}
}
-
/*
* Initializes the verifier object by reading all the manifest
* entries and passing them to the verifier.
*/
private void initializeVerifier() {
--- 718,727 ----
*** 902,928 ****
}
private JarEntry getManEntry() {
if (manEntry == null) {
// First look up manifest entry using standard name
! ZipEntry manEntry = super.getEntry(MANIFEST_NAME);
if (manEntry == null) {
// If not found, then iterate through all the "META-INF/"
// entries to find a match.
String[] names = getMetaInfEntryNames();
if (names != null) {
for (String name : names) {
if (MANIFEST_NAME.equals(name.toUpperCase(Locale.ENGLISH))) {
! manEntry = super.getEntry(name);
break;
}
}
}
}
! this.manEntry = (manEntry == null)
! ? null
! : new JarFileEntry(manEntry.getName(), manEntry);
}
return manEntry;
}
/**
--- 917,941 ----
}
private JarEntry getManEntry() {
if (manEntry == null) {
// First look up manifest entry using standard name
! JarEntry manEntry = getEntry0(MANIFEST_NAME);
if (manEntry == null) {
// If not found, then iterate through all the "META-INF/"
// entries to find a match.
String[] names = getMetaInfEntryNames();
if (names != null) {
for (String name : names) {
if (MANIFEST_NAME.equals(name.toUpperCase(Locale.ENGLISH))) {
! manEntry = getEntry0(name);
break;
}
}
}
}
! this.manEntry = manEntry;
}
return manEntry;
}
/**
*** 1030,1041 ****
initializeVerifier();
jvInitialized = true;
}
}
! JarEntry newEntry(ZipEntry ze) {
! return new JarFileEntry(ze);
}
Enumeration<String> entryNames(CodeSource[] cs) {
ensureInitialization();
if (jv != null) {
--- 1043,1078 ----
initializeVerifier();
jvInitialized = true;
}
}
! /*
! * Returns a versioned {@code JarFileEntry} for the given entry,
! * if there is one. Otherwise returns the original entry. This
! * is invoked by the {@code entries2} for verifier.
! */
! JarEntry newEntry(JarEntry je) {
! if (isMultiRelease()) {
! return getVersionedEntry(je.getName(), je);
! }
! return je;
! }
!
! /*
! * Returns a versioned {@code JarFileEntry} for the given entry
! * name, if there is one. Otherwise returns a {@code JarFileEntry}
! * with the given name. It is invoked from JarVerifier's entries2
! * for {@code singers}.
! */
! JarEntry newEntry(String name) {
! if (isMultiRelease()) {
! JarEntry vje = getVersionedEntry(name, (JarEntry)null);
! if (vje != null) {
! return vje;
! }
! }
! return new JarFileEntry(name);
}
Enumeration<String> entryNames(CodeSource[] cs) {
ensureInitialization();
if (jv != null) {
*** 1075,1113 ****
* signed entries missing from the ZIP directory.
*/
Enumeration<JarEntry> entries2() {
ensureInitialization();
if (jv != null) {
! return jv.entries2(this, super.entries());
}
// screen out entries which are never signed
! final Enumeration<? extends ZipEntry> enum_ = super.entries();
return new Enumeration<>() {
! ZipEntry entry;
public boolean hasMoreElements() {
if (entry != null) {
return true;
}
! while (enum_.hasMoreElements()) {
! ZipEntry ze = enum_.nextElement();
! if (JarVerifier.isSigningRelated(ze.getName())) {
continue;
}
! entry = ze;
return true;
}
return false;
}
! public JarFileEntry nextElement() {
if (hasMoreElements()) {
! ZipEntry ze = entry;
entry = null;
! return new JarFileEntry(ze);
}
throw new NoSuchElementException();
}
};
}
--- 1112,1152 ----
* signed entries missing from the ZIP directory.
*/
Enumeration<JarEntry> entries2() {
ensureInitialization();
if (jv != null) {
! return jv.entries2(this, JUZFA.entries(JarFile.this,
! JarFileEntry::new));
}
// screen out entries which are never signed
! final var unfilteredEntries = JUZFA.entries(JarFile.this, JarFileEntry::new);
!
return new Enumeration<>() {
! JarEntry entry;
public boolean hasMoreElements() {
if (entry != null) {
return true;
}
! while (unfilteredEntries.hasMoreElements()) {
! JarEntry je = unfilteredEntries.nextElement();
! if (JarVerifier.isSigningRelated(je.getName())) {
continue;
}
! entry = je;
return true;
}
return false;
}
! public JarEntry nextElement() {
if (hasMoreElements()) {
! JarEntry je = entry;
entry = null;
! return newEntry(je);
}
throw new NoSuchElementException();
}
};
}