From 6808dd6d2b2daa214543098d57869f95b4635838 Mon Sep 17 00:00:00 2001 From: Plamen Totev Date: Sun, 9 Dec 2018 11:03:23 +0200 Subject: [PATCH] Improve PlexusIoZipFileResourceCollection performance by using JarFile Currently `PlexusIoZipFileResourceCollection` uses `PlexusIoURLResource` to get the `InputStream` of the JAR entries. `PlexusIoURLResource` uses `URL` and `URLConnection` to get the input stream. The problem is that they create a new `JarFile` for every entry and in some cases the `JarFile` initialization could be expensive (for example when he JAR is signed). Using the `URLConnection` cache would solve the performance issues but opens new one. The cache is global for the build so if the JAR file have changed during the build you may get the cached instance (see plexus-io#2). Modify `PlexusIoZipFileResourceCollection` to use `JarFile` directly instead of using `PlexusIoURLResource`. That would solve solves the two issues - `JarFile` is initialized once so there is no performance penalty and it is local so if the file changed during the build the latest version will be picked up. Under the hood `PlexusIoURLResource` uses `JarFile` as well and keeping `ZipFileResource` extending `PlexusIoURLResource` would both keep the behaviour and interface backward compatible. Closes #106 --- .../PlexusIoZipFileResourceCollection.java | 50 ++++++++++++++++--- 1 file changed, 42 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/codehaus/plexus/archiver/zip/PlexusIoZipFileResourceCollection.java b/src/main/java/org/codehaus/plexus/archiver/zip/PlexusIoZipFileResourceCollection.java index e14a7ebe2..90c970bc3 100644 --- a/src/main/java/org/codehaus/plexus/archiver/zip/PlexusIoZipFileResourceCollection.java +++ b/src/main/java/org/codehaus/plexus/archiver/zip/PlexusIoZipFileResourceCollection.java @@ -18,11 +18,13 @@ import java.io.Closeable; import java.io.File; import java.io.IOException; +import java.io.InputStream; import java.net.URL; import java.net.URLClassLoader; import java.nio.charset.Charset; import java.util.Enumeration; import java.util.Iterator; +import java.util.jar.JarFile; import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; import org.apache.commons.compress.archivers.zip.ZipFile; import org.codehaus.plexus.components.io.functions.SymlinkDestinationSupplier; @@ -84,9 +86,10 @@ public URL getResource( String name ) }; final URL url = new URL( "jar:" + f.toURI().toURL() + "!/" ); + final JarFile jarFile = new JarFile( f ); final ZipFile zipFile = new ZipFile( f, charset != null ? charset.name() : "UTF8" ); final Enumeration en = zipFile.getEntriesInPhysicalOrder(); - return new ZipFileResourceIterator( en, url, zipFile, urlClassLoader ); + return new ZipFileResourceIterator( en, url, jarFile, zipFile, urlClassLoader ); } private static class ZipFileResourceIterator @@ -96,14 +99,35 @@ private static class ZipFileResourceIterator private class ZipFileResource extends PlexusIoURLResource { + private final JarFile jarFile; - private ZipFileResource( ZipArchiveEntry entry ) + private ZipFileResource( JarFile jarFile, ZipArchiveEntry entry ) { super( entry.getName(), entry.getTime() == -1 ? PlexusIoResource.UNKNOWN_MODIFICATION_DATE : entry.getTime(), entry.isDirectory() ? PlexusIoResource.UNKNOWN_RESOURCE_SIZE : entry.getSize(), !entry.isDirectory(), entry.isDirectory(), true ); + this.jarFile = jarFile; + } + + @Override + public InputStream getContents() throws IOException { + // Uses the JarFile to get the input stream for the entry. + // The super method will do the same, why overriding it? + // But it will create new JarFile for every entry + // and that could be very expensive in some cases (when the JAR is signed). + // Enabling the URLConnection cache would solve the problem + // but the cache is global and shared during the build so + // if the AJR file changed during the causes another problem + // (see plexus-io#2). + // Using local JarFile instance solves the two issues - + // JarFile is initialized once so there is no performance penalty + // and it is local so if the file changed during the build + // would not be a problem. + // And we know the URL returned by getURL is part of the JAR + // because that is how we constructed it. + return jarFile.getInputStream( jarFile.getEntry( getName() ) ); } @Override @@ -131,9 +155,9 @@ private class ZipFileSymlinkResource private final ZipArchiveEntry entry; - private ZipFileSymlinkResource( ZipArchiveEntry entry ) + private ZipFileSymlinkResource( JarFile jarFile, ZipArchiveEntry entry ) { - super( entry ); + super( jarFile, entry ); this.entry = entry; } @@ -157,15 +181,18 @@ public boolean isSymbolicLink() private final URL url; + private final JarFile jarFile; + private final ZipFile zipFile; private final URLClassLoader urlClassLoader; - public ZipFileResourceIterator( Enumeration en, URL url, ZipFile zipFile, + public ZipFileResourceIterator( Enumeration en, URL url, JarFile jarFile, ZipFile zipFile, URLClassLoader urlClassLoader ) { this.en = en; this.url = url; + this.jarFile = jarFile; this.zipFile = zipFile; this.urlClassLoader = urlClassLoader; } @@ -181,8 +208,8 @@ public PlexusIoResource next() { final ZipArchiveEntry entry = en.nextElement(); return entry.isUnixSymlink() - ? new ZipFileSymlinkResource( entry ) - : new ZipFileResource( entry ); + ? new ZipFileSymlinkResource( jarFile, entry ) + : new ZipFileResource( jarFile, entry ); } @@ -202,7 +229,14 @@ public void close() } finally { - zipFile.close(); + try + { + zipFile.close(); + } finally + { + jarFile.close(); + } + } }