This is an automated email from the ASF dual-hosted git repository. markt pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/tomcat-jakartaee-migration.git
commit fc2245dd5d7d605e77d31a33221fa4af5ce08f7d Author: Mark Thomas <ma...@apache.org> AuthorDate: Mon Feb 8 13:58:15 2021 +0000 Fix#5 Add a new -zipInMemory option --- CHANGES.md | 1 + pom.xml | 5 ++ .../org/apache/tomcat/jakartaee/Migration.java | 87 +++++++++++++++++++++- .../org/apache/tomcat/jakartaee/MigrationCLI.java | 9 +++ .../org/apache/tomcat/jakartaee/MigrationTask.java | 6 ++ .../tomcat/jakartaee/LocalStrings.properties | 3 +- 6 files changed, 108 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 5e9ed97..6e2b583 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -17,6 +17,7 @@ - Include the `javax.resource` package in the EE profile (ebourg) - Fix [#7](https://github.com/apache/tomcat-jakartaee-migration/issues/7). Include the `javax.jms` package in the EE profile (alitokmen/mgirgorov) - Make `migrate.sh` work from any path (mgrigorov) +- Add a new option `-zipInMemory` that processes archives (ZIP, JAR, WAR, etc.) in memory rather via a streaming approach. While less efficient, it allows archives to be processed when their structure means that a streaming approach will fail. ## 0.1.0 diff --git a/pom.xml b/pom.xml index d8290db..5b4b1ec 100644 --- a/pom.xml +++ b/pom.xml @@ -73,6 +73,11 @@ <version>6.5.0</version> </dependency> <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-compress</artifactId> + <version>1.20</version> + </dependency> + <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.8.0</version> diff --git a/src/main/java/org/apache/tomcat/jakartaee/Migration.java b/src/main/java/org/apache/tomcat/jakartaee/Migration.java index 69a23c0..2489844 100644 --- a/src/main/java/org/apache/tomcat/jakartaee/Migration.java +++ b/src/main/java/org/apache/tomcat/jakartaee/Migration.java @@ -16,6 +16,7 @@ */ package org.apache.tomcat.jakartaee; +import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; @@ -24,6 +25,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; +import java.util.Enumeration; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -36,7 +38,13 @@ import java.util.jar.JarOutputStream; import java.util.jar.Manifest; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.zip.ZipException; +import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; +import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream; +import org.apache.commons.compress.archivers.zip.ZipFile; +import org.apache.commons.compress.utils.SeekableInMemoryByteChannel; +import org.apache.commons.io.IOUtils; import org.apache.commons.io.input.CloseShieldInputStream; import org.apache.commons.io.output.CloseShieldOutputStream; @@ -46,6 +54,7 @@ public class Migration { private static final StringManager sm = StringManager.getManager(Migration.class); private EESpecProfile profile = EESpecProfile.TOMCAT; + private boolean zipInMemory; private File source; private File destination; private final List<Converter> converters; @@ -79,6 +88,10 @@ public class Migration { return profile; } + public void setZipInMemory(boolean zipInMemory) { + this.zipInMemory = zipInMemory; + } + public void setSource(File source) { if (!source.canRead()) { throw new IllegalArgumentException(sm.getString("migration.cannotReadSource", @@ -124,6 +137,7 @@ public class Migration { private boolean migrateDirectory(File src, File dest) throws IOException { boolean result = true; + // Won't return null because src is known to be a directory String[] files = src.list(); for (String file : files) { File srcFile = new File(src, file); @@ -168,7 +182,7 @@ public class Migration { } - private boolean migrateArchive(InputStream src, OutputStream dest) throws IOException { + private boolean migrateArchiveStreaming(String name, InputStream src, OutputStream dest) throws IOException { boolean result = true; try (JarInputStream jarIs = new JarInputStream(new CloseShieldInputStream(src)); JarOutputStream jarOs = new JarOutputStream(new CloseShieldOutputStream(dest))) { @@ -198,7 +212,59 @@ public class Migration { jarOs.putNextEntry(destEntry); result = result && migrateStream(destEntry.getName(), jarIs, jarOs); } + } catch (ZipException ze) { + logger.log(Level.SEVERE, sm.getString("migration.archiveFailed", name), ze); + throw ze; + } + return result; + } + + + private boolean migrateArchiveInMemory(InputStream src, OutputStream dest) throws IOException { + boolean result = true; + // Read the source into memory + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + IOUtils.copy(src, baos); + SeekableInMemoryByteChannel srcByteChannel = new SeekableInMemoryByteChannel(baos.toByteArray()); + // Create the destination in memory + SeekableInMemoryByteChannel destByteChannel = new SeekableInMemoryByteChannel(); + + try (ZipFile srcZipFile = new ZipFile(srcByteChannel); + ZipArchiveOutputStream destZipStream = new ZipArchiveOutputStream(destByteChannel)) { + Enumeration<ZipArchiveEntry> entries = srcZipFile.getEntries(); + while (entries.hasMoreElements()) { + ZipArchiveEntry srcZipEntry = entries.nextElement(); + String srcName = srcZipEntry.getName(); + logger.log(Level.FINE, sm.getString("migration.entry", srcName)); + if (srcZipEntry.getName().equals(JarFile.MANIFEST_NAME)) { + // Special handling for manifest + Manifest manifest = new Manifest(srcZipFile.getInputStream(srcZipEntry)); + updateVersion(manifest); + if (removeSignatures(manifest)) { + logger.log(Level.WARNING, sm.getString("migration.warnSignatureRemoval")); + } + destZipStream.putArchiveEntry(srcZipEntry); + manifest.write(destZipStream); + destZipStream.closeArchiveEntry(); + } else { + if (isSignatureFile(srcName)) { + logger.log(Level.FINE, sm.getString("migration.skipSignatureFile", srcName)); + continue; + } + String destName = profile.convert(srcName); + RenamableZipArchiveEntry destZipEntry = new RenamableZipArchiveEntry(srcZipEntry); + destZipEntry.setName(destName); + destZipStream.putArchiveEntry(destZipEntry); + result = result && migrateStream(srcName, srcZipFile.getInputStream(srcZipEntry), destZipStream); + destZipStream.closeArchiveEntry(); + } + } } + + // Write the destination back to the stream + ByteArrayInputStream bais = new ByteArrayInputStream(destByteChannel.array()); + IOUtils.copy(bais, dest); + return result; } @@ -212,7 +278,11 @@ public class Migration { private boolean migrateStream(String name, InputStream src, OutputStream dest) throws IOException { if (isArchive(name)) { logger.log(Level.INFO, sm.getString("migration.archive", name)); - return migrateArchive(src, dest); + if (zipInMemory) { + return migrateArchiveInMemory(src, dest); + } else { + return migrateArchiveStreaming(name, src, dest); + } } else { logger.log(Level.FINE, sm.getString("migration.stream", name)); for (Converter converter : converters) { @@ -275,4 +345,17 @@ public class Migration { private static boolean isArchive(String fileName) { return fileName.endsWith(".jar") || fileName.endsWith(".war") || fileName.endsWith(".zip"); } + + + private static class RenamableZipArchiveEntry extends ZipArchiveEntry { + + public RenamableZipArchiveEntry(ZipArchiveEntry entry) throws ZipException { + super(entry); + } + + @Override + public void setName(String name) { + super.setName(name); + } + } } diff --git a/src/main/java/org/apache/tomcat/jakartaee/MigrationCLI.java b/src/main/java/org/apache/tomcat/jakartaee/MigrationCLI.java index 8c33d2d..5c3bf4f 100644 --- a/src/main/java/org/apache/tomcat/jakartaee/MigrationCLI.java +++ b/src/main/java/org/apache/tomcat/jakartaee/MigrationCLI.java @@ -31,6 +31,8 @@ public class MigrationCLI { private static final String PROFILE_ARG = "-profile="; + private static final String ZIPINMEMORY_ARG = "-zipInMemory"; + public static void main(String[] args) { System.setProperty("java.util.logging.SimpleFormatter.format", "%5$s%n %6$s%n"); @@ -41,7 +43,14 @@ public class MigrationCLI { arguments.remove("-verbose"); } + boolean zipInMemory = false; + if (arguments.contains(ZIPINMEMORY_ARG)) { + zipInMemory = true; + arguments.remove(ZIPINMEMORY_ARG); + } + Migration migration = new Migration(); + migration.setZipInMemory(zipInMemory); boolean valid = false; String source = null; diff --git a/src/main/java/org/apache/tomcat/jakartaee/MigrationTask.java b/src/main/java/org/apache/tomcat/jakartaee/MigrationTask.java index 4d7d967..6bdda1d 100644 --- a/src/main/java/org/apache/tomcat/jakartaee/MigrationTask.java +++ b/src/main/java/org/apache/tomcat/jakartaee/MigrationTask.java @@ -32,6 +32,7 @@ public class MigrationTask extends Task { private File src; private File dest; private String profile = EESpecProfile.TOMCAT.toString(); + private boolean zipInMemory = false; public void setSrc(File src) { this.src = src; @@ -45,6 +46,10 @@ public class MigrationTask extends Task { this.profile = profile; } + public void setZipInMemory(boolean zipInMemory) { + this.zipInMemory = zipInMemory; + } + @Override public void execute() throws BuildException { // redirect the log messages to Ant @@ -67,6 +72,7 @@ public class MigrationTask extends Task { migration.setSource(src); migration.setDestination(dest); migration.setEESpecProfile(profile); + migration.setZipInMemory(zipInMemory); boolean success = false; try { diff --git a/src/main/resources/org/apache/tomcat/jakartaee/LocalStrings.properties b/src/main/resources/org/apache/tomcat/jakartaee/LocalStrings.properties index 93a7649..cf62ca3 100644 --- a/src/main/resources/org/apache/tomcat/jakartaee/LocalStrings.properties +++ b/src/main/resources/org/apache/tomcat/jakartaee/LocalStrings.properties @@ -14,6 +14,7 @@ # limitations under the License. migration.archive=Migrating archive [{0}] +migration.archiveFailed=Failed to migrate archive [{0}]. Using the "-zipInMemory" option may help. migration.cannotReadSource=Cannot read source location [{0}] migration.done=Migration completed successfully [{1}] in [{0}] milliseconds migration.entry=Migrating Jar entry [{0}] @@ -23,5 +24,5 @@ migration.mkdirError=Error creating destination directory [{0}] migration.removeSignature=Remove cryptographic signature for [{0}] migration.skipSignatureFile=Drop cryptographic signature file [{0}] migration.stream=Migrating stream [{0}] -migration.usage=Usage: Migration [-profile=TOMCAT|-profile=EE] <source> <destination> +migration.usage=Usage: Migration [-profile=TOMCAT|-profile=EE] [-zipInMemory] [-verbose] <source> <destination> migration.warnSignatureRemoval=Removed cryptographic signature from JAR file --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org