diff --git a/src/it/MWAR-446/pom.xml b/src/it/MWAR-446/pom.xml new file mode 100644 index 00000000..7f4342ba --- /dev/null +++ b/src/it/MWAR-446/pom.xml @@ -0,0 +1,47 @@ + + + + 4.0.0 + foo.bar + MWAR-446 + 0.0.1-SNAPSHOT + war + + + + @project.groupId@ + @project.artifactId@ + @project.version@ + + false + false + + + + + + + org.codehaus.plexus + plexus-utils + @plexusUtilVersion@ + + + diff --git a/src/it/MWAR-446/src/main/webapp/index.html b/src/it/MWAR-446/src/main/webapp/index.html new file mode 100644 index 00000000..d236da5c --- /dev/null +++ b/src/it/MWAR-446/src/main/webapp/index.html @@ -0,0 +1,23 @@ + + +Hello World + diff --git a/src/it/MWAR-446/verify.groovy b/src/it/MWAR-446/verify.groovy new file mode 100644 index 00000000..092fa574 --- /dev/null +++ b/src/it/MWAR-446/verify.groovy @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.util.zip.ZipEntry +import java.util.zip.ZipFile + +def warFile = new File(basedir, 'target/MWAR-446-0.0.1-SNAPSHOT.war') +assert warFile.exists() : "WAR file should exist" + +def zipFile = new ZipFile(warFile) +try { + def foundStoredJar = false + def foundDeflatedEntry = false + + zipFile.entries().each { ZipEntry entry -> + if (entry.name.startsWith('WEB-INF/lib/') && entry.name.endsWith('.jar')) { + assert entry.method == ZipEntry.STORED : + "JAR entry ${entry.name} should be STORED (method=0) but was method=${entry.method}" + foundStoredJar = true + } + if (entry.name == 'index.html') { + assert entry.method == ZipEntry.DEFLATED : + "index.html should be DEFLATED (method=8) but was method=${entry.method}" + foundDeflatedEntry = true + } + } + + assert foundStoredJar : "Should have found at least one JAR in WEB-INF/lib/" + assert foundDeflatedEntry : "Should have found index.html" +} finally { + zipFile.close() +} diff --git a/src/main/java/org/apache/maven/plugins/war/AbstractWarMojo.java b/src/main/java/org/apache/maven/plugins/war/AbstractWarMojo.java index 56b1e6c0..144739dd 100644 --- a/src/main/java/org/apache/maven/plugins/war/AbstractWarMojo.java +++ b/src/main/java/org/apache/maven/plugins/war/AbstractWarMojo.java @@ -311,6 +311,18 @@ public abstract class AbstractWarMojo extends AbstractMojo { @Parameter(defaultValue = "true") private boolean recompressZippedFiles; + /** + * Whether dependency libraries (i.e. JAR files in {@code WEB-INF/lib/}) should be compressed (DEFLATED) when + * added to the WAR archive. Setting this to {@code false} will store the library JARs without compression + * (STORED mode), which can improve servlet container startup time since the JARs do not need to be decompressed. + * Note that since JAR files are already compressed, storing them without additional compression typically has + * negligible impact on the overall WAR file size. + * + * @since 3.5.2 + */ + @Parameter(defaultValue = "true") + private boolean compressLibs; + /** * @since 2.4 */ @@ -1052,6 +1064,14 @@ protected boolean isRecompressZippedFiles() { return recompressZippedFiles; } + /** + * @return {@link #compressLibs} + * @since 3.5.2 + */ + protected boolean isCompressLibs() { + return compressLibs; + } + /** * @return {@link #includeEmptyDirectories} */ diff --git a/src/main/java/org/apache/maven/plugins/war/WarMojo.java b/src/main/java/org/apache/maven/plugins/war/WarMojo.java index c68aac3c..8cb8d616 100644 --- a/src/main/java/org/apache/maven/plugins/war/WarMojo.java +++ b/src/main/java/org/apache/maven/plugins/war/WarMojo.java @@ -43,11 +43,13 @@ import org.apache.maven.project.MavenProjectHelper; import org.apache.maven.shared.filtering.MavenFileFilter; import org.apache.maven.shared.filtering.MavenResourcesFiltering; +import org.codehaus.plexus.archiver.ArchiveEntry; import org.codehaus.plexus.archiver.ArchiverException; import org.codehaus.plexus.archiver.jar.ManifestException; import org.codehaus.plexus.archiver.manager.ArchiverManager; import org.codehaus.plexus.archiver.manager.NoSuchArchiverException; import org.codehaus.plexus.archiver.war.WarArchiver; +import org.codehaus.plexus.archiver.zip.ConcurrentJarCreator; import org.codehaus.plexus.util.FileUtils; /** @@ -386,12 +388,45 @@ public void setWarName(String warName) { public WarArchiver getWarArchiver() { try { - return (WarArchiver) getArchiverManager().getArchiver("war"); + if (isCompressLibs()) { + return (WarArchiver) getArchiverManager().getArchiver("war"); + } else { + return new UncompressedLibsWarArchiver(); + } } catch (NoSuchArchiverException e) { throw new IllegalStateException("Cannot find war archiver", e); } } + /** + * A {@link WarArchiver} subclass that stores dependency library JARs ({@code WEB-INF/lib/*.jar}) + * in STORED mode (uncompressed) instead of DEFLATED mode, while still compressing all other entries. + * This improves servlet container startup time since the already-compressed JARs are not + * decompressed and recompressed during WAR creation and extraction. + */ + private static class UncompressedLibsWarArchiver extends WarArchiver { + + UncompressedLibsWarArchiver() { + super(); + } + + @Override + protected void zipFile(ArchiveEntry entry, ConcurrentJarCreator zOut, String vPath) + throws IOException, ArchiverException { + if (vPath.startsWith("WEB-INF/lib/") && !vPath.endsWith("/")) { + boolean original = isCompress(); + setCompress(false); + try { + super.zipFile(entry, zOut, vPath); + } finally { + setCompress(original); + } + } else { + super.zipFile(entry, zOut, vPath); + } + } + } + /** * @return {@link #projectHelper} */