summaryrefslogtreecommitdiff
path: root/src/reflect/scala/reflect/io/ZipArchive.scala
diff options
context:
space:
mode:
Diffstat (limited to 'src/reflect/scala/reflect/io/ZipArchive.scala')
-rw-r--r--src/reflect/scala/reflect/io/ZipArchive.scala82
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)