diff options
Diffstat (limited to 'src/reflect/scala/reflect/io/ZipArchive.scala')
-rw-r--r-- | src/reflect/scala/reflect/io/ZipArchive.scala | 82 |
1 files changed, 61 insertions, 21 deletions
diff --git a/src/reflect/scala/reflect/io/ZipArchive.scala b/src/reflect/scala/reflect/io/ZipArchive.scala index 0c63acb86c..f4e1633af4 100644 --- a/src/reflect/scala/reflect/io/ZipArchive.scala +++ b/src/reflect/scala/reflect/io/ZipArchive.scala @@ -12,8 +12,8 @@ import java.io.{ IOException, InputStream, ByteArrayInputStream, FilterInputStre import java.io.{ File => JFile } import java.util.zip.{ ZipEntry, ZipFile, ZipInputStream } import java.util.jar.Manifest -import scala.collection.{ immutable, mutable } -import scala.collection.convert.WrapAsScala.asScalaIterator +import scala.collection.mutable +import scala.collection.JavaConverters._ import scala.annotation.tailrec /** An abstraction for zip files and streams. Everything is written the way @@ -27,6 +27,8 @@ import scala.annotation.tailrec * ''Note: This library is considered experimental and should not be used unless you know what you are doing.'' */ object ZipArchive { + private[io] val closeZipFile = sys.props.get("scala.classpath.closeZip").map(_.toBoolean).getOrElse(false) + /** * @param file a File * @return A ZipArchive if `file` is a readable zip file, otherwise null. @@ -120,31 +122,69 @@ abstract class ZipArchive(override val file: JFile) extends AbstractFile with Eq } /** ''Note: This library is considered experimental and should not be used unless you know what you are doing.'' */ final class FileZipArchive(file: JFile) extends ZipArchive(file) { + private[this] def openZipFile(): ZipFile = try { + new ZipFile(file) + } catch { + case ioe: IOException => throw new IOException("Error accessing " + file.getPath, ioe) + } + + private[this] class LazyEntry( + name: String, + time: Long, + size: Int + ) extends Entry(name) { + override def lastModified: Long = time // could be stale + override def input: InputStream = { + val zipFile = openZipFile() + val entry = zipFile.getEntry(name) + val delegate = zipFile.getInputStream(entry) + new FilterInputStream(delegate) { + override def close(): Unit = { zipFile.close() } + } + } + override def sizeOption: Option[Int] = Some(size) // could be stale + } + + // keeps a file handle open to ZipFile, which forbids file mutation + // on Windows, and leaks memory on all OS (typically by stopping + // classloaders from being garbage collected). But is slightly + // faster than LazyEntry. + private[this] class LeakyEntry( + zipFile: ZipFile, + zipEntry: ZipEntry + ) extends Entry(zipEntry.getName) { + override def lastModified: Long = zipEntry.getTime + override def input: InputStream = zipFile.getInputStream(zipEntry) + override def sizeOption: Option[Int] = Some(zipEntry.getSize.toInt) + } + lazy val (root, allDirs) = { val root = new DirEntry("/") val dirs = mutable.HashMap[String, DirEntry]("/" -> root) - val zipFile = try { - new ZipFile(file) - } catch { - case ioe: IOException => throw new IOException("Error accessing " + file.getPath, ioe) - } - + val zipFile = openZipFile() val enum = zipFile.entries() - while (enum.hasMoreElements) { - val zipEntry = enum.nextElement - val dir = getDir(dirs, zipEntry) - if (zipEntry.isDirectory) dir - else { - class FileEntry() extends Entry(zipEntry.getName) { - override def getArchive = zipFile - override def lastModified = zipEntry.getTime() - override def input = getArchive getInputStream zipEntry - override def sizeOption = Some(zipEntry.getSize().toInt) + try { + while (enum.hasMoreElements) { + val zipEntry = enum.nextElement + val dir = getDir(dirs, zipEntry) + if (zipEntry.isDirectory) dir + else { + val f = + if (ZipArchive.closeZipFile) + new LazyEntry( + zipEntry.getName(), + zipEntry.getTime(), + zipEntry.getSize().toInt + ) + else + new LeakyEntry(zipFile, zipEntry) + + dir.entries(f.name) = f } - val f = new FileEntry() - dir.entries(f.name) = f } + } finally { + if (ZipArchive.closeZipFile) zipFile.close() } (root, dirs) } @@ -238,7 +278,7 @@ final class ManifestResources(val url: URL) extends ZipArchive(null) { val root = new DirEntry("/") val dirs = mutable.HashMap[String, DirEntry]("/" -> root) val manifest = new Manifest(input) - val iter = manifest.getEntries().keySet().iterator().filter(_.endsWith(".class")).map(new ZipEntry(_)) + val iter = manifest.getEntries().keySet().iterator().asScala.filter(_.endsWith(".class")).map(new ZipEntry(_)) for (zipEntry <- iter) { val dir = getDir(dirs, zipEntry) |