src/share/classes/java/util/zip/ZipFile.java

Print this page

        

*** 29,43 **** import java.io.InputStream; import java.io.IOException; import java.io.EOFException; import java.io.File; import java.nio.charset.Charset; ! import java.util.Vector; import java.util.Enumeration; ! import java.util.Set; ! import java.util.HashSet; import java.util.NoSuchElementException; import java.security.AccessController; import sun.security.action.GetPropertyAction; import static java.util.zip.ZipConstants64.*; /** --- 29,45 ---- import java.io.InputStream; import java.io.IOException; import java.io.EOFException; import java.io.File; import java.nio.charset.Charset; ! import java.util.ArrayDeque; ! import java.util.Deque; import java.util.Enumeration; ! import java.util.HashMap; ! import java.util.Map; import java.util.NoSuchElementException; + import java.util.WeakHashMap; import java.security.AccessController; import sun.security.action.GetPropertyAction; import static java.util.zip.ZipConstants64.*; /**
*** 52,62 **** public class ZipFile implements ZipConstants, Closeable { private long jzfile; // address of jzfile data private String name; // zip file name private int total; // total number of entries ! private boolean closeRequested; private static final int STORED = ZipEntry.STORED; private static final int DEFLATED = ZipEntry.DEFLATED; /** --- 54,64 ---- public class ZipFile implements ZipConstants, Closeable { private long jzfile; // address of jzfile data private String name; // zip file name private int total; // total number of entries ! private volatile boolean closeRequested = false; private static final int STORED = ZipEntry.STORED; private static final int DEFLATED = ZipEntry.DEFLATED; /**
*** 312,323 **** boolean addSlash); // freeEntry releases the C jzentry struct. private static native void freeEntry(long jzfile, long jzentry); ! // the outstanding inputstreams that need to be closed. ! private Set<InputStream> streams = new HashSet<>(); /** * Returns an input stream for reading the contents of the specified * zip file entry. * --- 314,326 ---- boolean addSlash); // freeEntry releases the C jzentry struct. private static native void freeEntry(long jzfile, long jzentry); ! // the outstanding inputstreams that need to be closed, ! // mapped to the inflater objects they use. ! private final Map<InputStream, Inflater> streams = new WeakHashMap<>(); /** * Returns an input stream for reading the contents of the specified * zip file entry. *
*** 349,440 **** } in = new ZipFileInputStream(jzentry); switch (getEntryMethod(jzentry)) { case STORED: ! streams.add(in); return in; case DEFLATED: - final ZipFileInputStream zfin = in; // MORE: Compute good size for inflater stream: long size = getEntrySize(jzentry) + 2; // Inflater likes a bit of slack if (size > 65536) size = 8192; if (size <= 0) size = 4096; ! InputStream is = new InflaterInputStream(zfin, getInflater(), (int)size) { ! private boolean isClosed = false; public void close() throws IOException { ! if (!isClosed) { super.close(); releaseInflater(inf); - isClosed = true; } } // Override fill() method to provide an extra "dummy" byte // at the end of the input stream. This is required when // using the "nowrap" Inflater option. protected void fill() throws IOException { if (eof) { ! throw new EOFException( ! "Unexpected end of ZLIB input stream"); } ! len = this.in.read(buf, 0, buf.length); if (len == -1) { buf[0] = 0; len = 1; eof = true; } inf.setInput(buf, 0, len); } - private boolean eof; public int available() throws IOException { ! if (isClosed) return 0; long avail = zfin.size() - inf.getBytesWritten(); ! return avail > (long) Integer.MAX_VALUE ? ! Integer.MAX_VALUE : (int) avail; ! } ! }; ! streams.add(is); ! return is; ! default: ! throw new ZipException("invalid compression method"); } } } /* * Gets an inflater from the list of available inflaters or allocates * a new one. */ private Inflater getInflater() { ! synchronized (inflaters) { ! int size = inflaters.size(); ! if (size > 0) { ! Inflater inf = (Inflater)inflaters.remove(size - 1); return inf; - } else { - return new Inflater(true); } } } /* * Releases the specified inflater to the list of available inflaters. */ private void releaseInflater(Inflater inf) { ! synchronized (inflaters) { ! if (inf.ended()) ! return; inf.reset(); ! inflaters.add(inf); } } // List of available Inflater objects for decompression ! private Vector inflaters = new Vector(); /** * Returns the path name of the ZIP file. * @return the path name of the ZIP file */ --- 352,468 ---- } in = new ZipFileInputStream(jzentry); switch (getEntryMethod(jzentry)) { case STORED: ! synchronized (streams) { ! streams.put(in, null); ! } return in; case DEFLATED: // MORE: Compute good size for inflater stream: long size = getEntrySize(jzentry) + 2; // Inflater likes a bit of slack if (size > 65536) size = 8192; if (size <= 0) size = 4096; ! Inflater inf = getInflater(); ! InputStream is = ! new ZipFileInflaterInputStream(in, inf, (int)size); ! synchronized (streams) { ! streams.put(is, inf); ! } ! return is; ! default: ! throw new ZipException("invalid compression method"); ! } ! } ! } ! ! private class ZipFileInflaterInputStream extends InflaterInputStream { ! private volatile boolean closeRequested = false; ! private boolean eof = false; ! private final ZipFileInputStream zfin; ! ! ZipFileInflaterInputStream(ZipFileInputStream zfin, Inflater inf, ! int size) { ! super(zfin, inf, size); ! this.zfin = zfin; ! } public void close() throws IOException { ! if (closeRequested) ! return; ! closeRequested = true; ! super.close(); + Inflater inf; + synchronized (streams) { + inf = streams.remove(this); + } + if (inf != null) { releaseInflater(inf); } } + // Override fill() method to provide an extra "dummy" byte // at the end of the input stream. This is required when // using the "nowrap" Inflater option. protected void fill() throws IOException { if (eof) { ! throw new EOFException("Unexpected end of ZLIB input stream"); } ! len = in.read(buf, 0, buf.length); if (len == -1) { buf[0] = 0; len = 1; eof = true; } inf.setInput(buf, 0, len); } public int available() throws IOException { ! if (closeRequested) return 0; long avail = zfin.size() - inf.getBytesWritten(); ! return (avail > (long) Integer.MAX_VALUE ? ! Integer.MAX_VALUE : (int) avail); } + + protected void finalize() throws Throwable { + close(); } } /* * Gets an inflater from the list of available inflaters or allocates * a new one. */ private Inflater getInflater() { ! Inflater inf; ! synchronized (inflaterCache) { ! while (null != (inf = inflaterCache.poll())) { ! if (false == inf.ended()) { return inf; } } } + return new Inflater(true); + } /* * Releases the specified inflater to the list of available inflaters. */ private void releaseInflater(Inflater inf) { ! if (false == inf.ended()) { inf.reset(); ! synchronized (inflaterCache) { ! inflaterCache.add(inf); ! } } } // List of available Inflater objects for decompression ! private Deque<Inflater> inflaterCache = new ArrayDeque<>(); /** * Returns the path name of the ZIP file. * @return the path name of the ZIP file */
*** 538,580 **** * getInputStream} method. * * @throws IOException if an I/O error has occurred */ public void close() throws IOException { ! synchronized (this) { closeRequested = true; ! if (streams.size() !=0) { ! Set<InputStream> copy = streams; ! streams = new HashSet<>(); ! for (InputStream is: copy) ! is.close(); } if (jzfile != 0) { // Close the zip file long zf = this.jzfile; jzfile = 0; close(zf); - - // Release inflaters - synchronized (inflaters) { - int size = inflaters.size(); - for (int i = 0; i < size; i++) { - Inflater inf = (Inflater)inflaters.get(i); - inf.end(); - } } } } - } - /** ! * Ensures that the <code>close</code> method of this ZIP file is ! * called when there are no more references to it. * * <p> * Since the time when GC would invoke this method is undetermined, * it is strongly recommended that applications invoke the <code>close</code> * method as soon they have finished accessing this <code>ZipFile</code>. --- 566,616 ---- * getInputStream} method. * * @throws IOException if an I/O error has occurred */ public void close() throws IOException { ! if (closeRequested) ! return; closeRequested = true; ! synchronized (this) { ! // Close streams, release their inflaters ! synchronized (streams) { ! if (false == streams.isEmpty()) { ! Map<InputStream, Inflater> copy = new HashMap<>(streams); ! streams.clear(); ! for (Map.Entry<InputStream, Inflater> e : copy.entrySet()) { ! e.getKey().close(); ! Inflater inf = e.getValue(); ! if (inf != null) { ! inf.end(); ! } ! } ! } ! } ! ! // Release cached inflaters ! Inflater inf; ! synchronized (inflaterCache) { ! while (null != (inf = inflaterCache.poll())) { ! inf.end(); ! } } if (jzfile != 0) { // Close the zip file long zf = this.jzfile; jzfile = 0; close(zf); } } } /** ! * Ensures that the system resources held by this ZipFile object are ! * released when there are no more references to it. * * <p> * Since the time when GC would invoke this method is undetermined, * it is strongly recommended that applications invoke the <code>close</code> * method as soon they have finished accessing this <code>ZipFile</code>.
*** 609,618 **** --- 645,655 ---- /* * Inner class implementing the input stream used to read a * (possibly compressed) zip file entry. */ private class ZipFileInputStream extends InputStream { + private volatile boolean closeRequested = false; protected long jzentry; // address of jzentry data private long pos; // current position within entry data protected long rem; // number of remaining bytes within entry protected long size; // uncompressed size of this entry
*** 676,694 **** --- 713,741 ---- public long size() { return size; } public void close() { + if (closeRequested) + return; + closeRequested = true; + rem = 0; synchronized (ZipFile.this) { if (jzentry != 0 && ZipFile.this.jzfile != 0) { freeEntry(ZipFile.this.jzfile, jzentry); jzentry = 0; } + } + synchronized (streams) { streams.remove(this); } } + + protected void finalize() { + close(); + } } private static native long open(String name, int mode, long lastModified, boolean usemmap) throws IOException;