[Git][java-team/tomcat-jakartaee-migration][master] 3 commits: New upstream version 0.2.0

Emmanuel Bourg gitlab at salsa.debian.org
Sun Feb 28 20:21:31 GMT 2021



Emmanuel Bourg pushed to branch master at Debian Java Maintainers / tomcat-jakartaee-migration


Commits:
b184ff42 by Emmanuel Bourg at 2021-02-28T21:11:46+01:00
New upstream version 0.2.0
- - - - -
6b8616d5 by Emmanuel Bourg at 2021-02-28T21:11:47+01:00
Update upstream source from tag 'upstream/0.2.0'

Update to upstream version '0.2.0'
with Debian dir 5f91872754880be229eca958b0785e9153e93440
- - - - -
865db0d4 by Emmanuel Bourg at 2021-02-28T21:17:57+01:00
New upstream release (0.2.0)

- - - - -


22 changed files:

- + CHANGES.md
- NOTICE.txt
- README.md
- debian/changelog
- pom.xml
- src/assembly/src.xml
- src/main/java/org/apache/tomcat/jakartaee/ClassConverter.java
- src/main/java/org/apache/tomcat/jakartaee/Converter.java
- src/main/java/org/apache/tomcat/jakartaee/EESpecProfile.java
- + src/main/java/org/apache/tomcat/jakartaee/GlobMatcher.java
- + src/main/java/org/apache/tomcat/jakartaee/ManifestConverter.java
- src/main/java/org/apache/tomcat/jakartaee/Migration.java
- src/main/java/org/apache/tomcat/jakartaee/MigrationCLI.java
- src/main/java/org/apache/tomcat/jakartaee/MigrationTask.java
- src/main/java/org/apache/tomcat/jakartaee/PassThroughConverter.java
- src/main/java/org/apache/tomcat/jakartaee/TextConverter.java
- src/main/resources/org/apache/tomcat/jakartaee/LocalStrings.properties
- src/main/scripts/migrate.sh
- src/test/java/org/apache/tomcat/jakartaee/EESpecProfileTest.java
- src/test/java/org/apache/tomcat/jakartaee/MigrationTest.java
- src/test/java/org/apache/tomcat/jakartaee/PassThroughConverterTest.java
- src/test/java/org/apache/tomcat/jakartaee/TextConverterTest.java


Changes:

=====================================
CHANGES.md
=====================================
@@ -0,0 +1,33 @@
+# Tomcat Migration Tool for Jakarta EE - Changelog
+
+## 0.2.0 (in progress)
+
+- Add this changelog (markt)
+- Update dependencies (Apache Commons IO 2.8.0, Apache Ant 1.10.9) (markt)
+- Fix [#9](https://github.com/apache/tomcat-jakartaee-migration/issues/9). Exclude the `javax.xml.namespace` package in the EE profile (ebourg)
+- Include the `javax.management.j2ee` package in the EE profile (ebourg)
+- Add a test to confirm `javax.xml.xpath.XPathConstants` is not converted (ebourg)
+- Update README to mention the tool is now available on Debian/Ubuntu (ebourg)
+- Include the `javax.security.enterprise` package in the EE profile (ebourg)
+- Include the `javax.xml.registry` package in the EE profile (ebourg)
+- Include the `javax.security.jacc` package in the EE profile (ebourg)
+- Include the `javax.faces` package in the EE profile (ebourg)
+- Include the `javax.batch` package in the EE profile (ebourg)
+- Include the `javax.jws` package in the EE profile (ebourg)
+- 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. (markt)
+- Include the Maven Wrapper source files in the source distribution. (markt)
+- Ensure that all the Manifest attributes are processed during the migration process. (markt)
+- Include `.properties` and `.json` files in the conversion process. (markt)
+- Replace `-verbose` with `-logLevel=` to provide more control over logging level. (markt)
+- Fix [#13](https://github.com/apache/tomcat-jakartaee-migration/issues/13). Refactor mapping of log messages to log levels. (markt)
+- Fix [#3](https://github.com/apache/tomcat-jakartaee-migration/issues/3). Add support for excluding files from conversion. (markt)
+- Fix handling of classes with more than 32768 entries in the constant pool. (markt)
+- Exclude `javax.xml.stream` and `javax.xml.XMLConstants` from the EE profile. (markt)
+- Relocate dependencies under the `org.apache.tomcat.jakartaee` package to avoid clashes when integrating the shaded jar. (markt)
+
+## 0.1.0
+
+- Initial release (markt)


=====================================
NOTICE.txt
=====================================
@@ -1,5 +1,5 @@
 Apache Tomcat migration tool for Jakarta EE
-Copyright 2020 The Apache Software Foundation
+Copyright 2021 The Apache Software Foundation
 
 This product includes software developed at
 The Apache Software Foundation (https://www.apache.org/).
\ No newline at end of file


=====================================
README.md
=====================================
@@ -39,6 +39,10 @@ the same type as the source.
 >
 > A warning will be logged for each JAR file where the signature has been removed.
 
+This tool is also available on Debian and Ubuntu systems by installing the
+[tomcat-jakartaee-migration](https://tracker.debian.org/tomcat-jakartaee-migration)
+package and invoking the `javax2jakarta` command.
+
 ## Ant task
 
 The migration tool is available as an Ant task, here is an example:
@@ -49,13 +53,17 @@ The migration tool is available as an Ant task, here is an example:
 
 ## Differences between Java EE 8 and Jakarta EE 9
 
-Jakarta EE 9 is still under development and there are some details that remain
-to be worked out.
-
-The differences currently supported by this tool are:
+The difference between Java EE 8 and Jakarta EE 9 is that all the
+[Java EE 8 packages](https://github.com/apache/tomcat-jakartaee-migration/blob/master/src/main/java/org/apache/tomcat/jakartaee/EESpecProfile.java#L37)
+in the `javax.*` namespace have moved to the `jakarta.*` namespace.
+Some sub-packages have also been renamed. 
+This migration tool performs all the necessary changes to migrate an application
+from Java EE 8 to Jakarta EE 9 by renaming each Java EE 8 package to its Jakarta
+EE 9 replacement.
 
-* Renaming packages for Jakarta EE 9 APIs from `javax.*` to `jakarta.*`
+Note: Not all `javax.*` packages are part of Java EE. Only those defined by Java
+EE have moved to the `jakarta.*` namespace.
 
-Note: It will not be necessary to migrate any references to XML schemas. The
-schemas don't directly reference javax packages and Jakarta EE 9 will continue
-to support the use of schemas from Java EE 8 and earlier.
+Note: It is not necessary to migrate any references to XML schemas. The schemas
+don't directly reference javax packages and Jakarta EE 9 will continue to
+support the use of schemas from Java EE 8 and earlier.


=====================================
debian/changelog
=====================================
@@ -1,3 +1,9 @@
+tomcat-jakartaee-migration (0.2.0-1) unstable; urgency=medium
+
+  * New upstream release
+
+ -- Emmanuel Bourg <ebourg at apache.org>  Sun, 28 Feb 2021 21:17:45 +0100
+
 tomcat-jakartaee-migration (0.1.0-1) unstable; urgency=medium
 
   * New upstream release


=====================================
pom.xml
=====================================
@@ -26,7 +26,8 @@
 
   <groupId>org.apache.tomcat</groupId>
   <artifactId>jakartaee-migration</artifactId>
-  <version>0.1.0</version>
+  <name>Apache Tomcat Migration Tool for Jakarta EE</name>
+  <version>0.2.0</version>
  
   <description>
     This tool is a work in progress.
@@ -72,15 +73,20 @@
       <artifactId>bcel</artifactId>
       <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.6</version>
+      <version>2.8.0</version>
     </dependency>
     <dependency>
       <groupId>org.apache.ant</groupId>
       <artifactId>ant</artifactId>
-      <version>1.10.8</version>
+      <version>1.10.9</version>
       <scope>provided</scope>
     </dependency>
 
@@ -97,7 +103,7 @@
     <connection>scm:git:https://gitbox.apache.org/repos/asf/tomcat-jakartaee-migration.git</connection>
     <developerConnection>scm:git:https://gitbox.apache.org/repos/asf/tomcat-jakartaee-migration.git</developerConnection>
     <url>https://gitbox.apache.org/repos/asf?p=tomcat-jakartaee-migration.git</url>
-    <tag>0.1.0</tag>
+    <tag>0.2.0</tag>
   </scm>
 
   <build>
@@ -254,6 +260,12 @@
                     <exclude>META-INF/**</exclude>
                   </excludes>
                 </filter>
+                <filter>
+                  <artifact>org.apache.commons:*</artifact>
+                  <excludes>
+                    <exclude>META-INF/**</exclude>
+                  </excludes>
+                </filter>
                 <filter>
                   <artifact>commons-io:*</artifact>
                   <excludes>
@@ -261,6 +273,16 @@
                   </excludes>
                 </filter>
               </filters>
+              <relocations>
+                <relocation>
+                  <pattern>org.apache.commons</pattern>
+                  <shadedPattern>org.apache.tomcat.jakartaee.commons</shadedPattern>
+                </relocation>
+                <relocation>
+                  <pattern>org.apache.bcel</pattern>
+                  <shadedPattern>org.apache.tomcat.jakartaee.bcel</shadedPattern>
+                </relocation>
+              </relocations>
             </configuration>
           </execution>
         </executions>


=====================================
src/assembly/src.xml
=====================================
@@ -35,5 +35,11 @@
     <fileSet>
       <directory>src</directory>
     </fileSet>
+    <fileSet>
+      <directory>.mvn</directory>
+      <excludes>
+        <exclude>**/*.jar</exclude>
+      </excludes>
+    </fileSet>
   </fileSets>
 </assembly>


=====================================
src/main/java/org/apache/tomcat/jakartaee/ClassConverter.java
=====================================
@@ -16,16 +16,26 @@
  */
 package org.apache.tomcat.jakartaee;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.lang.instrument.ClassFileTransformer;
+import java.lang.instrument.IllegalClassFormatException;
+import java.security.ProtectionDomain;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 import org.apache.bcel.classfile.ClassParser;
 import org.apache.bcel.classfile.Constant;
 import org.apache.bcel.classfile.ConstantUtf8;
 import org.apache.bcel.classfile.JavaClass;
 
-public class ClassConverter implements Converter {
+public class ClassConverter implements Converter, ClassFileTransformer {
+
+    private static final Logger logger = Logger.getLogger(ClassConverter.class.getCanonicalName());
+    private static final StringManager sm = StringManager.getManager(ClassConverter.class);
 
     @Override
     public boolean accepts(String filename) {
@@ -35,22 +45,54 @@ public class ClassConverter implements Converter {
 
 
     @Override
-    public void convert(InputStream src, OutputStream dest, EESpecProfile profile) throws IOException {
+    public void convert(String path, InputStream src, OutputStream dest, EESpecProfile profile) throws IOException {
 
         ClassParser parser = new ClassParser(src, "unknown");
         JavaClass javaClass = parser.parse();
 
+        boolean converted = false;
+
         // Loop through constant pool
         Constant[] constantPool = javaClass.getConstantPool().getConstantPool();
-        for (short i = 0; i < constantPool.length; i++) {
+        // Need an int as the maximum pool size is 2^16
+        for (int i = 0; i < constantPool.length; i++) {
             if (constantPool[i] instanceof ConstantUtf8) {
                 ConstantUtf8 c = (ConstantUtf8) constantPool[i];
                 String str = c.getBytes();
-                c = new ConstantUtf8(profile.convert(str));
-                constantPool[i] = c;
+                String newString = profile.convert(str);
+                // Object comparison is deliberate
+                if (newString != str) {
+                    c = new ConstantUtf8(profile.convert(str));
+                    constantPool[i] = c;
+                    converted = true;
+                }
+            }
+        }
+
+        if (logger.isLoggable(Level.FINE)) {
+            if (converted) {
+                logger.log(Level.FINE, sm.getString("classConverter.converted", path));
+            } else if (logger.isLoggable(Level.FINEST)) {
+                logger.log(Level.FINEST, sm.getString("classConverter.noConversion", path));
             }
         }
 
         javaClass.dump(dest);
     }
+
+
+    @Override
+    public byte[] transform(ClassLoader loader, String className,
+            Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
+            byte[] classfileBuffer) throws IllegalClassFormatException {
+        ByteArrayInputStream inputStream = new ByteArrayInputStream(classfileBuffer);
+        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+        try {
+            convert(className, inputStream, outputStream, EESpecProfile.TOMCAT);
+        } catch (IOException e) {
+            throw new IllegalClassFormatException(e.getLocalizedMessage());
+        }
+        return outputStream.toByteArray();
+    }
+
 }


=====================================
src/main/java/org/apache/tomcat/jakartaee/Converter.java
=====================================
@@ -24,5 +24,16 @@ public interface Converter {
 
     boolean accepts(String filename);
 
-    void convert(InputStream src, OutputStream dest, EESpecProfile profile) throws IOException;
+    /**
+     * Copies the source to the destination, converting it if necessary,
+     * according to the requirements of the given profile.
+     *
+     * @param path      The path to the data being converted
+     * @param src       The source data to convert
+     * @param dest      The destination to write the converted data
+     * @param profile   The profile that defines the conversion required
+     *
+     * @throws IOException  If the conversion fails
+     */
+    void convert(String path, InputStream src, OutputStream dest, EESpecProfile profile) throws IOException;
 }


=====================================
src/main/java/org/apache/tomcat/jakartaee/EESpecProfile.java
=====================================
@@ -24,11 +24,40 @@ import java.util.regex.Pattern;
  */
 public enum EESpecProfile {
 
-    TOMCAT("javax([/\\.](annotation(?![/\\.]processing)|ejb|el|mail|persistence|security[/\\.]auth[/\\.]message|servlet|transaction(?![/\\.]xa)|websocket))"),
+    TOMCAT("javax([/\\.](annotation(?![/\\.]processing)" +
+            "|ejb" +
+            "|el" +
+            "|mail" +
+            "|persistence" +
+            "|security[/\\.]auth[/\\.]message" +
+            "|servlet" +
+            "|transaction(?![/\\.]xa)" +
+            "|websocket))"),
 
-    EE("javax([/\\.](activation|annotation(?![/\\.]processing)|decorator|ejb|el|enterprise|json|interceptor|inject|mail|persistence|"
-                + "security[/\\.]auth[/\\.]message|servlet|transaction(?![/\\.]xa)|validation|websocket|ws[/\\.]rs|"
-                + "xml[/\\.](bind|namespace|rpc|soap|stream|ws|XMLConstants)))");
+    EE("javax([/\\.](activation" +
+            "|annotation(?![/\\.]processing)" +
+            "|batch" +
+            "|decorator" +
+            "|ejb" +
+            "|el" +
+            "|enterprise" +
+            "|faces" +
+            "|jms" +
+            "|json" +
+            "|jws" +
+            "|interceptor" +
+            "|inject" +
+            "|mail" +
+            "|management[/\\.]j2ee" +
+            "|persistence" +
+            "|resource" +
+            "|security[/\\.](auth[/\\.]message|enterprise|jacc)" +
+            "|servlet" +
+            "|transaction(?![/\\.]xa)" +
+            "|validation" +
+            "|websocket" +
+            "|ws[/\\.]rs" +
+            "|xml[/\\.](bind|registry|rpc|soap|ws)))");
 
     private Pattern pattern;
 


=====================================
src/main/java/org/apache/tomcat/jakartaee/GlobMatcher.java
=====================================
@@ -0,0 +1,243 @@
+/*
+ *  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.
+ *
+ */
+
+package org.apache.tomcat.jakartaee;
+
+import java.util.Set;
+
+/**
+ * <p>This is a utility class to match file globs.
+ * The class has been derived from
+ * org.apache.tools.ant.types.selectors.SelectorUtils.
+ * </p>
+ * <p>All methods are static.</p>
+ */
+public final class GlobMatcher {
+
+    /**
+     * Tests whether or not a given file name matches any file name pattern in
+     * the given set. The match is performed case-sensitively.
+     *
+     * @see #match(String, String, boolean)
+     *
+     * @param patternSet The pattern set to match against. Must not be
+     *                <code>null</code>.
+     * @param fileName The file name to match, as a String. Must not be
+     *                <code>null</code>. It must be just a file name, without
+     *                a path.
+     * @param caseSensitive Whether or not matching should be performed
+     *                        case sensitively.
+     *
+     * @return <code>true</code> if any pattern in the set matches against the
+     *         file name, or <code>false</code> otherwise.
+     */
+    public static boolean matchName(Set<String> patternSet, String fileName, boolean caseSensitive) {
+        char[] fileNameArray = fileName.toCharArray();
+        for (String pattern: patternSet) {
+            if (match(pattern, fileNameArray, caseSensitive)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+
+    /**
+     * Tests whether or not a string matches against a pattern.
+     * The pattern may contain two special characters:<br>
+     * '*' means zero or more characters<br>
+     * '?' means one and only one character
+     *
+     * @param pattern The pattern to match against.
+     *                Must not be <code>null</code>.
+     * @param str     The string which must be matched against the
+     *                pattern. Must not be <code>null</code>.
+     * @param caseSensitive Whether or not matching should be performed
+     *                        case sensitively.
+     *
+     *
+     * @return <code>true</code> if the string matches against the pattern,
+     *         or <code>false</code> otherwise.
+     */
+    public static boolean match(String pattern, String str,
+            boolean caseSensitive) {
+
+        return match(pattern, str.toCharArray(), caseSensitive);
+    }
+
+
+    /**
+     * Tests whether or not a string matches against a pattern.
+     * The pattern may contain two special characters:<br>
+     * '*' means zero or more characters<br>
+     * '?' means one and only one character
+     *
+     * @param pattern The pattern to match against.
+     *                Must not be <code>null</code>.
+     * @param strArr  The character array which must be matched against the
+     *                pattern. Must not be <code>null</code>.
+     * @param caseSensitive Whether or not matching should be performed
+     *                        case sensitively.
+     *
+     *
+     * @return <code>true</code> if the string matches against the pattern,
+     *         or <code>false</code> otherwise.
+     */
+    private static boolean match(String pattern, char[] strArr,
+                                boolean caseSensitive) {
+        char[] patArr = pattern.toCharArray();
+        int patIdxStart = 0;
+        int patIdxEnd = patArr.length - 1;
+        int strIdxStart = 0;
+        int strIdxEnd = strArr.length - 1;
+        char ch;
+
+        boolean containsStar = false;
+        for (char c : patArr) {
+            if (c == '*') {
+                containsStar = true;
+                break;
+            }
+        }
+
+        if (!containsStar) {
+            // No '*'s, so we make a shortcut
+            if (patIdxEnd != strIdxEnd) {
+                return false; // Pattern and string do not have the same size
+            }
+            for (int i = 0; i <= patIdxEnd; i++) {
+                ch = patArr[i];
+                if (ch != '?') {
+                    if (different(caseSensitive, ch, strArr[i])) {
+                        return false; // Character mismatch
+                    }
+                }
+            }
+            return true; // String matches against pattern
+        }
+
+        if (patIdxEnd == 0) {
+            return true; // Pattern contains only '*', which matches anything
+        }
+
+        // Process characters before first star
+        while (true) {
+            ch = patArr[patIdxStart];
+            if (ch == '*' || strIdxStart > strIdxEnd) {
+                break;
+            }
+            if (ch != '?') {
+                if (different(caseSensitive, ch, strArr[strIdxStart])) {
+                    return false; // Character mismatch
+                }
+            }
+            patIdxStart++;
+            strIdxStart++;
+        }
+        if (strIdxStart > strIdxEnd) {
+            // All characters in the string are used. Check if only '*'s are
+            // left in the pattern. If so, we succeeded. Otherwise failure.
+            return allStars(patArr, patIdxStart, patIdxEnd);
+        }
+
+        // Process characters after last star
+        while (true) {
+            ch = patArr[patIdxEnd];
+            if (ch == '*' || strIdxStart > strIdxEnd) {
+                break;
+            }
+            if (ch != '?') {
+                if (different(caseSensitive, ch, strArr[strIdxEnd])) {
+                    return false; // Character mismatch
+                }
+            }
+            patIdxEnd--;
+            strIdxEnd--;
+        }
+        if (strIdxStart > strIdxEnd) {
+            // All characters in the string are used. Check if only '*'s are
+            // left in the pattern. If so, we succeeded. Otherwise failure.
+            return allStars(patArr, patIdxStart, patIdxEnd);
+        }
+
+        // process pattern between stars. padIdxStart and patIdxEnd point
+        // always to a '*'.
+        while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) {
+            int patIdxTmp = -1;
+            for (int i = patIdxStart + 1; i <= patIdxEnd; i++) {
+                if (patArr[i] == '*') {
+                    patIdxTmp = i;
+                    break;
+                }
+            }
+            if (patIdxTmp == patIdxStart + 1) {
+                // Two stars next to each other, skip the first one.
+                patIdxStart++;
+                continue;
+            }
+            // Find the pattern between padIdxStart & padIdxTmp in str between
+            // strIdxStart & strIdxEnd
+            int patLength = (patIdxTmp - patIdxStart - 1);
+            int strLength = (strIdxEnd - strIdxStart + 1);
+            int foundIdx = -1;
+            strLoop:
+            for (int i = 0; i <= strLength - patLength; i++) {
+                for (int j = 0; j < patLength; j++) {
+                    ch = patArr[patIdxStart + j + 1];
+                    if (ch != '?') {
+                        if (different(caseSensitive, ch,
+                                      strArr[strIdxStart + i + j])) {
+                            continue strLoop;
+                        }
+                    }
+                }
+
+                foundIdx = strIdxStart + i;
+                break;
+            }
+
+            if (foundIdx == -1) {
+                return false;
+            }
+
+            patIdxStart = patIdxTmp;
+            strIdxStart = foundIdx + patLength;
+        }
+
+        // All characters in the string are used. Check if only '*'s are left
+        // in the pattern. If so, we succeeded. Otherwise failure.
+        return allStars(patArr, patIdxStart, patIdxEnd);
+    }
+
+    private static boolean allStars(char[] chars, int start, int end) {
+        for (int i = start; i <= end; ++i) {
+            if (chars[i] != '*') {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private static boolean different(
+        boolean caseSensitive, char ch, char other) {
+        return caseSensitive
+            ? ch != other
+            : Character.toUpperCase(ch) != Character.toUpperCase(other);
+    }
+
+}


=====================================
src/main/java/org/apache/tomcat/jakartaee/ManifestConverter.java
=====================================
@@ -0,0 +1,128 @@
+/*
+ * 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.
+ */
+package org.apache.tomcat.jakartaee;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.jar.Attributes;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Updates Manifests.
+ */
+public class ManifestConverter implements Converter {
+
+    private static final Logger logger = Logger.getLogger(ManifestConverter.class.getCanonicalName());
+    private static final StringManager sm = StringManager.getManager(ManifestConverter.class);
+
+    @Override
+    public boolean accepts(String filename) {
+        if (JarFile.MANIFEST_NAME.equals(filename)) {
+            return true;
+        }
+
+        // Reject everything else
+        return false;
+    }
+
+    @Override
+    public void convert(String path, InputStream src, OutputStream dest, EESpecProfile profile) throws IOException {
+        Manifest srcManifest = new Manifest(src);
+        Manifest destManifest = new Manifest(srcManifest);
+
+        boolean result = false;
+
+        result = result | removeSignatures(destManifest);
+        result = result | updateValues(destManifest, profile);
+
+        if (result) {
+            destManifest.write(dest);
+        } else {
+            srcManifest.write(dest);
+        }
+    }
+
+
+    private boolean removeSignatures(Manifest manifest) {
+        boolean removedSignatures = manifest.getMainAttributes().remove(Attributes.Name.SIGNATURE_VERSION) != null;
+        List<String> signatureEntries = new ArrayList<>();
+        Map<String, Attributes> manifestAttributeEntries = manifest.getEntries();
+        for (Entry<String, Attributes> entry : manifestAttributeEntries.entrySet()) {
+            if (isCryptoSignatureEntry(entry.getValue())) {
+                String entryName = entry.getKey();
+                signatureEntries.add(entryName);
+                logger.log(Level.FINE, sm.getString("migration.removeSignature", entryName));
+                removedSignatures = true;
+            }
+        }
+
+        for (String entry : signatureEntries) {
+            manifestAttributeEntries.remove(entry);
+        }
+
+        return removedSignatures;
+    }
+
+
+    private boolean isCryptoSignatureEntry(Attributes attributes) {
+        for (Object attributeKey : attributes.keySet()) {
+            if (attributeKey.toString().endsWith("-Digest")) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+
+    private boolean updateValues(Manifest manifest, EESpecProfile profile) {
+        boolean result = false;
+        result = result | updateValues(manifest.getMainAttributes(), profile);
+        for (Attributes attributes : manifest.getEntries().values()) {
+            result = result | updateValues(attributes, profile);
+        }
+        return result;
+    }
+
+
+    private boolean updateValues(Attributes attributes, EESpecProfile profile) {
+        boolean result = false;
+        // Update version info
+        if (attributes.containsKey(Attributes.Name.IMPLEMENTATION_VERSION)) {
+            String newValue = attributes.get(Attributes.Name.IMPLEMENTATION_VERSION) + "-" + Info.getVersion();
+            attributes.put(Attributes.Name.IMPLEMENTATION_VERSION, newValue);
+            result = true;
+        }
+        // Update package names in values
+        for (Entry<Object,Object> entry : attributes.entrySet()) {
+            String newValue = profile.convert((String) entry.getValue());
+            // Object comparison is deliberate
+            if (newValue != entry.getValue()) {
+                entry.setValue(newValue);
+                result = true;
+            }
+        }
+        return result;
+    }
+}


=====================================
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,19 +25,24 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashSet;
 import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
+import java.util.Set;
 import java.util.concurrent.TimeUnit;
-import java.util.jar.Attributes;
 import java.util.jar.JarEntry;
-import java.util.jar.JarFile;
-import java.util.jar.JarInputStream;
-import java.util.jar.JarOutputStream;
-import java.util.jar.Manifest;
 import java.util.logging.Level;
 import java.util.logging.Logger;
-
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipException;
+import java.util.zip.ZipInputStream;
+import java.util.zip.ZipOutputStream;
+
+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;
 
@@ -45,10 +51,50 @@ public class Migration {
     private static final Logger logger = Logger.getLogger(Migration.class.getCanonicalName());
     private static final StringManager sm = StringManager.getManager(Migration.class);
 
+    private static final Set<String> DEFAULT_EXCLUDES = new HashSet<>();
+
+    static {
+        // Apache Commons
+        DEFAULT_EXCLUDES.add("commons-codec-*.jar");
+        DEFAULT_EXCLUDES.add("commons-lang-*.jar");
+        // Apache HTTP Components
+        DEFAULT_EXCLUDES.add("httpclient-*.jar");
+        DEFAULT_EXCLUDES.add("httpcore-*.jar");
+        // ASM
+        DEFAULT_EXCLUDES.add("asm-*.jar");
+        // AspectJ
+        DEFAULT_EXCLUDES.add("aspectjweaver-*.jar");
+        // Bouncy Castle JCE provider
+        DEFAULT_EXCLUDES.add("bcprov*.jar");
+        DEFAULT_EXCLUDES.add("bcpkix*.jar");
+        // Closure compiler
+        DEFAULT_EXCLUDES.add("closure-compiler-*.jar");
+        // Eclipse compiler for Java
+        DEFAULT_EXCLUDES.add("ecj-*.jar");
+        // Hystrix
+        DEFAULT_EXCLUDES.add("hystrix-core-*.jar");
+        DEFAULT_EXCLUDES.add("hystrix-serialization-*.jar");
+        // Jackson
+        DEFAULT_EXCLUDES.add("jackson-annotations-*.jar");
+        DEFAULT_EXCLUDES.add("jackson-core-*.jar");
+        DEFAULT_EXCLUDES.add("jackson-module-afterburner-*.jar");
+        // Logging
+        DEFAULT_EXCLUDES.add("jul-to-slf4j-*.jar");
+        DEFAULT_EXCLUDES.add("log4j-to-slf4j-*.jar");
+        DEFAULT_EXCLUDES.add("slf4j-api-*.jar");
+        // Spring
+        DEFAULT_EXCLUDES.add("spring-aop-*.jar");
+        DEFAULT_EXCLUDES.add("spring-expression-*.jar");
+        DEFAULT_EXCLUDES.add("spring-security-crypto-*.jar");
+        DEFAULT_EXCLUDES.add("spring-security-rsa-*.jar");
+    }
+
     private EESpecProfile profile = EESpecProfile.TOMCAT;
+    private boolean zipInMemory;
     private File source;
     private File destination;
     private final List<Converter> converters;
+    private final Set<String> excludes = new HashSet<>();
 
     public Migration() {
         // Initialise the converters
@@ -56,6 +102,7 @@ public class Migration {
 
         converters.add(new TextConverter());
         converters.add(new ClassConverter());
+        converters.add(new ManifestConverter());
 
         // Final converter is the pass-through converter
         converters.add(new PassThroughConverter());
@@ -79,6 +126,14 @@ public class Migration {
         return profile;
     }
 
+    public void setZipInMemory(boolean zipInMemory) {
+        this.zipInMemory = zipInMemory;
+    }
+
+    public void addExclude(String exclude) {
+        this.excludes.add(exclude);
+    }
+
     public void setSource(File source) {
         if (!source.canRead()) {
             throw new IllegalArgumentException(sm.getString("migration.cannotReadSource",
@@ -93,113 +148,124 @@ public class Migration {
     }
 
 
-    public boolean execute() throws IOException {
+    public void execute() throws IOException {
         logger.log(Level.INFO, sm.getString("migration.execute", source.getAbsolutePath(),
                 destination.getAbsolutePath(), profile.toString()));
-        boolean result = true;
+
         long t1 = System.nanoTime();
         if (source.isDirectory()) {
             if ((destination.exists() && destination.isDirectory()) || destination.mkdirs()) {
-                result = result && migrateDirectory(source, destination);
+                migrateDirectory(source, destination);
             } else {
-                logger.log(Level.WARNING, sm.getString("migration.mkdirError", destination.getAbsolutePath()));
-                result = false;
+                throw new IOException(sm.getString("migration.mkdirError", destination.getAbsolutePath()));
             }
         } else {
             // Single file
             File parentDestination = destination.getAbsoluteFile().getParentFile();
             if (parentDestination.exists() || parentDestination.mkdirs()) {
-                result = result && migrateFile(source, destination);
+                migrateFile(source, destination);
             } else {
-                logger.log(Level.WARNING, sm.getString("migration.mkdirError", parentDestination.getAbsolutePath()));
-                result = false;
+                throw new IOException(sm.getString("migration.mkdirError", parentDestination.getAbsolutePath()));
             }
         }
         logger.log(Level.INFO, sm.getString("migration.done",
-                Long.valueOf(TimeUnit.MILLISECONDS.convert(System.nanoTime() - t1, TimeUnit.NANOSECONDS)),
-                Boolean.valueOf(result)));
-        return result;
+                Long.valueOf(TimeUnit.MILLISECONDS.convert(System.nanoTime() - t1, TimeUnit.NANOSECONDS))));
     }
 
 
-    private boolean migrateDirectory(File src, File dest) throws IOException {
-        boolean result = true;
+    private void migrateDirectory(File src, File dest) throws IOException {
+        // 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);
             File destFile = new File(dest, file);
             if (srcFile.isDirectory()) {
                 if ((destFile.exists() && destFile.isDirectory()) || destFile.mkdir()) {
-                    result = result && migrateDirectory(srcFile, destFile);
+                    migrateDirectory(srcFile, destFile);
                 } else {
-                    logger.log(Level.WARNING, sm.getString("migration.mkdirError", destFile.getAbsolutePath()));
-                    result = false;
+                    throw new IOException(sm.getString("migration.mkdirError", destFile.getAbsolutePath()));
                 }
             } else {
-                result = result && migrateFile(srcFile, destFile);
+                migrateFile(srcFile, destFile);
             }
         }
-        return result;
     }
 
 
-    private boolean migrateFile(File src, File dest) throws IOException {
-        boolean result = false;
-
+    private void migrateFile(File src, File dest) throws IOException {
         boolean inplace = src.equals(dest);
         if (!inplace) {
             try (InputStream is = new FileInputStream(src);
                  OutputStream os = new FileOutputStream(dest)) {
-                result = migrateStream(src.getName(), is, os);
+                migrateStream(src.getName(), is, os);
             }
         } else {
             ByteArrayOutputStream buffer = new ByteArrayOutputStream((int) (src.length() * 1.05));
 
             try (InputStream is = new FileInputStream(src)) {
-                result = migrateStream(src.getName(), is, buffer);
+                migrateStream(src.getName(), is, buffer);
             }
 
             try (OutputStream os = new FileOutputStream(dest)) {
                 os.write(buffer.toByteArray());
             }
         }
-
-        return result;
     }
 
 
-    private boolean migrateArchive(InputStream src, OutputStream dest) throws IOException {
-        boolean result = true;
-        try (JarInputStream jarIs = new JarInputStream(new CloseShieldInputStream(src));
-                JarOutputStream jarOs = new JarOutputStream(new CloseShieldOutputStream(dest))) {
-            Manifest manifest = jarIs.getManifest();
-            if (manifest != null) {
-                // Make a safe copy to leave original manifest untouched.
-                // Otherwise messing with signatures will fail
-                manifest = new Manifest(manifest);
-                updateVersion(manifest);
-                if (removeSignatures(manifest)) {
-                    logger.log(Level.WARNING, sm.getString("migration.warnSignatureRemoval"));
-                }
-                JarEntry manifestEntry = new JarEntry(JarFile.MANIFEST_NAME);
-                jarOs.putNextEntry(manifestEntry);
-                manifest.write(jarOs);
-            }
-            JarEntry jarEntry;
-            while ((jarEntry = jarIs.getNextJarEntry()) != null) {
-                String sourceName = jarEntry.getName();
-                logger.log(Level.FINE, sm.getString("migration.entry", sourceName));
+    private void migrateArchiveStreaming(String name, InputStream src, OutputStream dest) throws IOException {
+        try (ZipInputStream zipIs = new ZipInputStream(new CloseShieldInputStream(src));
+                ZipOutputStream zipOs = new ZipOutputStream(new CloseShieldOutputStream(dest))) {
+            ZipEntry zipEntry;
+            while ((zipEntry = zipIs.getNextEntry()) != null) {
+                String sourceName = zipEntry.getName();
                 if (isSignatureFile(sourceName)) {
-                    logger.log(Level.FINE, sm.getString("migration.skipSignatureFile", sourceName));
+                    logger.log(Level.WARNING, sm.getString("migration.skipSignatureFile", sourceName));
                     continue;
                 }
                 String destName = profile.convert(sourceName);
                 JarEntry destEntry = new JarEntry(destName);
-                jarOs.putNextEntry(destEntry);
-                result = result && migrateStream(destEntry.getName(), jarIs, jarOs);
+                zipOs.putNextEntry(destEntry);
+                migrateStream(sourceName, zipIs, zipOs);
+            }
+        } catch (ZipException ze) {
+            logger.log(Level.SEVERE, sm.getString("migration.archiveFailed", name), ze);
+            throw ze;
+        }
+    }
+
+
+    private void migrateArchiveInMemory(InputStream src, OutputStream dest) throws IOException {
+        // Read the source into memory
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        IOUtils.copy(src, baos);
+        baos.flush();
+        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();
+                if (isSignatureFile(srcName)) {
+                    logger.log(Level.WARNING, sm.getString("migration.skipSignatureFile", srcName));
+                    continue;
+                }
+                String destName = profile.convert(srcName);
+                RenamableZipArchiveEntry destZipEntry = new RenamableZipArchiveEntry(srcZipEntry);
+                destZipEntry.setName(destName);
+                destZipStream.putArchiveEntry(destZipEntry);
+                migrateStream(srcName, srcZipFile.getInputStream(srcZipEntry), destZipStream);
+                destZipStream.closeArchiveEntry();
             }
         }
-        return result;
+
+        // Write the destination back to the stream
+        ByteArrayInputStream bais = new ByteArrayInputStream(destByteChannel.array(), 0, (int) destByteChannel.size());
+        IOUtils.copy(bais, dest);
     }
 
 
@@ -209,70 +275,60 @@ 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);
+    private void migrateStream(String name, InputStream src, OutputStream dest) throws IOException {
+        if (isExcluded(name)) {
+            Util.copy(src, dest);
+            logger.log(Level.INFO, sm.getString("migration.skip", name));
+        } else if (isArchive(name)) {
+            if (zipInMemory) {
+                logger.log(Level.INFO, sm.getString("migration.archive.memory", name));
+                migrateArchiveInMemory(src, dest);
+                logger.log(Level.INFO, sm.getString("migration.archive.complete", name));
+            } else {
+                logger.log(Level.INFO, sm.getString("migration.archive.stream", name));
+                migrateArchiveStreaming(name, src, dest);
+                logger.log(Level.INFO, sm.getString("migration.archive.complete", name));
+            }
         } else {
-            logger.log(Level.FINE, sm.getString("migration.stream", name));
             for (Converter converter : converters) {
                 if (converter.accepts(name)) {
-                    converter.convert(src, dest, profile);
+                    converter.convert(name, src, dest, profile);
                     break;
                 }
             }
-            return true;
         }
     }
 
 
-    private boolean removeSignatures(Manifest manifest) {
-        boolean removedSignatures = manifest.getMainAttributes().remove(Attributes.Name.SIGNATURE_VERSION) != null;
-        List<String> signatureEntries = new ArrayList<>();
-        Map<String, Attributes> manifestAttributeEntries = manifest.getEntries();
-        for (Entry<String, Attributes> entry : manifestAttributeEntries.entrySet()) {
-            if (isCryptoSignatureEntry(entry.getValue())) {
-                String entryName = entry.getKey();
-                signatureEntries.add(entryName);
-                logger.log(Level.FINE, sm.getString("migration.removeSignature", entryName));
-                removedSignatures = true;
-            }
-        }
+    private boolean isArchive(String fileName) {
+        return fileName.endsWith(".jar") || fileName.endsWith(".war") || fileName.endsWith(".zip");
+    }
 
-        for (String entry : signatureEntries) {
-            manifestAttributeEntries.remove(entry);
-        }
 
-        return removedSignatures;
-    }
+    private boolean isExcluded(String name) {
+        File f = new File(name);
+        String filename = f.getName();
 
+        if (GlobMatcher.matchName(DEFAULT_EXCLUDES, filename, true)) {
+            return true;
+        }
 
-    private boolean isCryptoSignatureEntry(Attributes attributes) {
-        for (Object attributeKey : attributes.keySet()) {
-            if (attributeKey.toString().endsWith("-Digest")) {
-                return true;
-            }
+        if (GlobMatcher.matchName(excludes, filename, true)) {
+            return true;
         }
+
         return false;
     }
 
+    private static class RenamableZipArchiveEntry extends ZipArchiveEntry {
 
-    private void updateVersion(Manifest manifest) {
-        updateVersion(manifest.getMainAttributes());
-        for (Attributes attributes : manifest.getEntries().values()) {
-            updateVersion(attributes);
+        public RenamableZipArchiveEntry(ZipArchiveEntry entry) throws ZipException {
+            super(entry);
         }
-    }
 
-
-    private void updateVersion(Attributes attributes) {
-        if (attributes.containsKey(Attributes.Name.IMPLEMENTATION_VERSION)) {
-            String newValue = attributes.get(Attributes.Name.IMPLEMENTATION_VERSION) + "-" + Info.getVersion();
-            attributes.put(Attributes.Name.IMPLEMENTATION_VERSION, newValue);
+        @Override
+        public void setName(String name) {
+            super.setName(name);
         }
     }
-
-    private static boolean isArchive(String fileName) {
-        return fileName.endsWith(".jar") || fileName.endsWith(".war") || fileName.endsWith(".zip");
-    }
 }


=====================================
src/main/java/org/apache/tomcat/jakartaee/MigrationCLI.java
=====================================
@@ -20,73 +20,92 @@ import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Iterator;
 import java.util.List;
+import java.util.Locale;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
 public class MigrationCLI {
 
-    private static final Logger logger = Logger.getLogger(MigrationCLI.class.getCanonicalName());
     private static final StringManager sm = StringManager.getManager(MigrationCLI.class);
 
+    private static final String EXCLUDE_ARG = "-exclude=";
+    private static final String LOGLEVEL_ARG = "-logLevel=";
     private static final String PROFILE_ARG = "-profile=";
+    // Will be removed for 1.0.0
+    @Deprecated
+    private static final String VERBOSE_ARG = "-verbose";
+    private static final String ZIPINMEMORY_ARG = "-zipInMemory";
 
-    public static void main(String[] args) {
+    public static void main(String[] args) throws IOException {
+
+        // Defaults
         System.setProperty("java.util.logging.SimpleFormatter.format", "%5$s%n");
+        Migration migration = new Migration();
 
+        // Process argumnets
         List<String> arguments = new ArrayList<>(Arrays.asList(args));
-        if (arguments.contains("-verbose")) {
-            Logger.getGlobal().getParent().getHandlers()[0].setLevel(Level.FINE);
-            Logger.getGlobal().getParent().setLevel(Level.FINE);
-            arguments.remove("-verbose");
-        }
 
-        Migration migration = new Migration();
-
-        boolean valid = false;
-        String source = null;
-        String dest = null;
-        if (arguments.size() == 3) {
-            if (arguments.get(0).startsWith(PROFILE_ARG)) {
-                source = arguments.get(1);
-                dest = arguments.get(2);
-                valid = true;
+        // Process the custom log level if present
+        // Use an iterator so we can remove the log level argument if found
+        Iterator<String> iter = arguments.iterator();
+        while (iter.hasNext()) {
+            String argument = iter.next();
+            if (argument.startsWith(EXCLUDE_ARG)) {
+                iter.remove();
+                String exclude = argument.substring(EXCLUDE_ARG.length());
+                migration.addExclude(exclude);
+            } else if (argument.startsWith(LOGLEVEL_ARG)) {
+                iter.remove();
+                String logLevelName = argument.substring(LOGLEVEL_ARG.length());
+                Level level = null;
+                try {
+                    level = Level.parse(logLevelName.toUpperCase(Locale.ENGLISH));
+                } catch (IllegalArgumentException iae) {
+                    invalidArguments();
+                }
+                // Configure the explicit level
+                Logger.getGlobal().getParent().getHandlers()[0].setLevel(level);
+                Logger.getGlobal().getParent().setLevel(level);
+            } else if (argument.startsWith(PROFILE_ARG)) {
+                iter.remove();
+                String profileName = argument.substring(PROFILE_ARG.length());
                 try {
-                    migration.setEESpecProfile(EESpecProfile.valueOf(arguments.get(0).substring(PROFILE_ARG.length())));
+                    EESpecProfile profile = EESpecProfile.valueOf(profileName.toUpperCase(Locale.ENGLISH));
+                    migration.setEESpecProfile(profile);
                 } catch (IllegalArgumentException e) {
                     // Invalid profile value
-                    valid = false;
+                    invalidArguments();
+                }
+            } else if (argument.equals(ZIPINMEMORY_ARG)) {
+                iter.remove();
+                migration.setZipInMemory(true);
+            } else if (argument.equals(VERBOSE_ARG)) {
+                iter.remove();
+                // Ignore if LOGLEVEL_ARG has set something different
+                if (Logger.getGlobal().getParent().getLevel().equals(Level.INFO)) {
+                    Logger.getGlobal().getParent().getHandlers()[0].setLevel(Level.FINE);
+                    Logger.getGlobal().getParent().setLevel(Level.FINE);
                 }
             }
         }
-        if (arguments.size() == 2) {
-            source = arguments.get(0);
-            dest = arguments.get(1);
-            valid = true;
-        }
-        if (!valid) {
-            usage();
-            System.exit(1);
+
+        if (arguments.size() != 2) {
+            invalidArguments();
         }
 
+        String source = arguments.get(0);
+        String dest = arguments.get(1);
+
         migration.setSource(new File(source));
         migration.setDestination(new File(dest));
-        boolean result = false;
-        try {
-            result = migration.execute();
-        } catch (IOException e) {
-            logger.log(Level.SEVERE, sm.getString("migration.error"), e);
-            result = false;
-        }
 
-        // Signal caller that migration failed
-        if (!result) {
-            System.exit(1);
-        }
+        migration.execute();
     }
 
-    private static void usage() {
+    private static void invalidArguments() {
         System.out.println(sm.getString("migration.usage"));
+        System.exit(1);
     }
-    
 }


=====================================
src/main/java/org/apache/tomcat/jakartaee/MigrationTask.java
=====================================
@@ -32,6 +32,8 @@ public class MigrationTask extends Task {
     private File src;
     private File dest;
     private String profile = EESpecProfile.TOMCAT.toString();
+    private boolean zipInMemory = false;
+    private String excludes;
 
     public void setSrc(File src) {
         this.src = src;
@@ -45,6 +47,20 @@ public class MigrationTask extends Task {
         this.profile = profile;
     }
 
+    public void setZipInMemory(boolean zipInMemory) {
+        this.zipInMemory = zipInMemory;
+    }
+
+    /**
+     * Set exclusion patterns.
+     *
+     * @param excludes  Comma separated, case sensitive list of glob patterns
+     *                  for files to exclude
+     */
+    public void setExcludes(String excludes) {
+        this.excludes = excludes;
+    }
+
     @Override
     public void execute() throws BuildException {
         // redirect the log messages to Ant
@@ -67,16 +83,18 @@ public class MigrationTask extends Task {
         migration.setSource(src);
         migration.setDestination(dest);
         migration.setEESpecProfile(profile);
+        migration.setZipInMemory(zipInMemory);
+        if (this.excludes != null) {
+            String[] excludes= this.excludes.split(",");
+            for (String exclude : excludes) {
+                migration.addExclude(exclude);
+            }
+        }
 
-        boolean success = false;
         try {
-            success = migration.execute();
+            migration.execute();
         } catch (IOException e) {
             throw new BuildException(e, getLocation());
         }
-
-        if (!success) {
-            throw new BuildException("Migration failed", getLocation());
-        }
     }
 }


=====================================
src/main/java/org/apache/tomcat/jakartaee/PassThroughConverter.java
=====================================
@@ -19,9 +19,14 @@ package org.apache.tomcat.jakartaee;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 public class PassThroughConverter implements Converter {
 
+    private static final Logger logger = Logger.getLogger(PassThroughConverter.class.getCanonicalName());
+    private static final StringManager sm = StringManager.getManager(PassThroughConverter.class);
+
     @Override
     public boolean accepts(String filename) {
         // Accepts everything
@@ -29,8 +34,11 @@ public class PassThroughConverter implements Converter {
     }
 
     @Override
-    public void convert(InputStream src, OutputStream dest, EESpecProfile profile) throws IOException {
+    public void convert(String path, InputStream src, OutputStream dest, EESpecProfile profile) throws IOException {
         // This simply copies the source to the destination
         Util.copy(src, dest);
+        if (logger.isLoggable(Level.FINEST)) {
+            logger.log(Level.FINEST, sm.getString("passThroughConverter.noConversion", path));
+        }
     }
 }


=====================================
src/main/java/org/apache/tomcat/jakartaee/TextConverter.java
=====================================
@@ -23,9 +23,14 @@ import java.io.OutputStream;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 public class TextConverter implements Converter {
 
+    private static final Logger logger = Logger.getLogger(TextConverter.class.getCanonicalName());
+    private static final StringManager sm = StringManager.getManager(TextConverter.class);
+
     private static final List<String> supportedExtensions;
 
     static {
@@ -38,6 +43,8 @@ public class TextConverter implements Converter {
         supportedExtensions.add("tld");
         supportedExtensions.add("txt");
         supportedExtensions.add("xml");
+        supportedExtensions.add("json");
+        supportedExtensions.add("properties");
     }
 
 
@@ -58,10 +65,21 @@ public class TextConverter implements Converter {
      * execution.
      */
     @Override
-    public void convert(InputStream src, OutputStream dest, EESpecProfile profile) throws IOException {
+    public void convert(String path, InputStream src, OutputStream dest, EESpecProfile profile) throws IOException {
         String srcString = Util.toString(src, StandardCharsets.ISO_8859_1);
         String destString = profile.convert(srcString);
 
+        // Object comparison is deliberate here
+        if (srcString == destString) {
+            if (logger.isLoggable(Level.FINEST)) {
+                logger.log(Level.FINEST, sm.getString("classConverter.noConversion", path));
+            }
+        } else {
+            if (logger.isLoggable(Level.FINE)) {
+                logger.log(Level.FINE, sm.getString("textConverter.converted", path));
+            }
+        }
+
         ByteArrayInputStream bais = new ByteArrayInputStream(destString.getBytes(StandardCharsets.ISO_8859_1));
         Util.copy(bais, dest);
     }


=====================================
src/main/resources/org/apache/tomcat/jakartaee/LocalStrings.properties
=====================================
@@ -13,15 +13,41 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-migration.archive=Migrating archive [{0}]
+classConverter.converted=Migrated class [{0}]
+classConverter.noConversion=No conversion necessary for [{0}] 
+
+migration.archive.complete=Migration finished for archive [{0}]
+migration.archive.memory=Migration starting for archive [{0}] using in memory copy
+migration.skip=Migration skipped for archive [{0}] because it is excluded (the archive was copied unchanged)
+migration.archive.stream=Migration starting for archive [{0}] using streaming
+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}]
+migration.done=Migration completed successfully in [{0}] milliseconds
 migration.error=Error performing migration
 migration.execute=Performing migration from source [{0}] to destination [{1}] with Jakarta EE specification profile [{2}]
 migration.mkdirError=Error creating destination directory [{0}]
 migration.removeSignature=Remove cryptographic signature for [{0}]
+migration.skip=Migration skipped for archive [{0}] because it is excluded (the archive was copied unchanged)
 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 [options] <source> <destination>\n\
+where options includes:\n\
+\    -exclude=<glob pattern to exclude>\n\
+\                This option may be used multiple times. Wild cards '*'\n\
+\                and '?' are supported. Matching is case sensitive.\n\
+\    -logLevel=<name of java.util.logging.level enum value>\n\
+\                Useful values are INFO (default), FINE or FINEST\n\
+\    -profile=<profile name>\n\
+\                TOMCAT (default) to convert Java EE APIs provided by Tomcat\n\
+\                EE to convert all Java EE APIs\n\
+\    -zipInMemory\n\
+\                By default zip format archives (.zip, jar, .war, .ear, etc.)\n\
+\                are processed in memory. This is more efficient but is not\n\
+\                compatible with some zip archive structures. If you see an\n\
+\                exception while processing a zip file, enabling this option\n\
+\                may workaround the issue. 
 migration.warnSignatureRemoval=Removed cryptographic signature from JAR file
+
+passThroughConverter.noConversion=No conversion necessary for [{0}] 
+
+textConverter.converted=Migrated text file [{0}]
+textConverter.noConversion=No conversion necessary for [{0}] 


=====================================
src/main/scripts/migrate.sh
=====================================
@@ -1,4 +1,6 @@
 #!/bin/sh
 
+BIN_FOLDER=`dirname "$0"`
+
 # Assumes java is on the path
-java -cp "../lib/*" org.apache.tomcat.jakartaee.MigrationCLI "$@"
+java -cp "$BIN_FOLDER/../lib/*" org.apache.tomcat.jakartaee.MigrationCLI "$@"


=====================================
src/test/java/org/apache/tomcat/jakartaee/EESpecProfileTest.java
=====================================
@@ -39,27 +39,38 @@ public class EESpecProfileTest {
 
         // not converted EE packages
         assertEquals("javax.activation", profile.convert("javax.activation"));
+        assertEquals("javax.batch", profile.convert("javax.batch"));
         assertEquals("javax.decorator", profile.convert("javax.decorator"));
         assertEquals("javax.enterprise", profile.convert("javax.enterprise"));
+        assertEquals("javax.faces", profile.convert("javax.faces"));
+        assertEquals("javax.jms", profile.convert("javax.jms"));
         assertEquals("javax.json", profile.convert("javax.json"));
+        assertEquals("javax.jws", profile.convert("javax.jws"));
         assertEquals("javax.interceptor", profile.convert("javax.interceptor"));
         assertEquals("javax.inject", profile.convert("javax.inject"));
+        assertEquals("javax.management.j2ee", profile.convert("javax.management.j2ee"));
+        assertEquals("javax.resource", profile.convert("javax.resource"));
+        assertEquals("javax.security.enterprise", profile.convert("javax.security.enterprise"));
+        assertEquals("javax.security.jacc", profile.convert("javax.security.jacc"));
         assertEquals("javax.validation", profile.convert("javax.validation"));
         assertEquals("javax.ws.rs", profile.convert("javax.ws.rs"));
         assertEquals("javax.xml.bind", profile.convert("javax.xml.bind"));
-        assertEquals("javax.xml.namespace", profile.convert("javax.xml.namespace"));
         assertEquals("javax.xml.rpc", profile.convert("javax.xml.rpc"));
+        assertEquals("javax.xml.registry", profile.convert("javax.xml.registry"));
         assertEquals("javax.xml.soap", profile.convert("javax.xml.soap"));
-        assertEquals("javax.xml.stream", profile.convert("javax.xml.stream"));
         assertEquals("javax.xml.ws", profile.convert("javax.xml.ws"));
-        assertEquals("javax.xml.XMLConstants", profile.convert("javax.xml.XMLConstants"));
 
         // non EE javax packages
         assertEquals("javax.annotation.processing", profile.convert("javax.annotation.processing"));
+        assertEquals("javax.management", profile.convert("javax.management"));
         assertEquals("javax.security", profile.convert("javax.security"));
         assertEquals("javax.security.auth", profile.convert("javax.security.auth"));
         assertEquals("javax.swing", profile.convert("javax.swing"));
         assertEquals("javax.transaction.xa", profile.convert("javax.transaction.xa"));
+        assertEquals("javax.xml.stream", profile.convert("javax.xml.stream"));
+        assertEquals("javax.xml.namespace", profile.convert("javax.xml.namespace"));
+        assertEquals("javax.xml.xpath.XPathConstants", profile.convert("javax.xml.xpath.XPathConstants"));
+        assertEquals("javax.xml.XMLConstants", profile.convert("javax.xml.XMLConstants"));
     }
 
     @Test
@@ -68,34 +79,45 @@ public class EESpecProfileTest {
 
         assertEquals("jakarta.activation", profile.convert("javax.activation"));
         assertEquals("jakarta.annotation", profile.convert("javax.annotation"));
+        assertEquals("jakarta.batch", profile.convert("javax.batch"));
         assertEquals("jakarta.decorator", profile.convert("javax.decorator"));
         assertEquals("jakarta.ejb", profile.convert("javax.ejb"));
         assertEquals("jakarta.el", profile.convert("javax.el"));
         assertEquals("jakarta.enterprise", profile.convert("javax.enterprise"));
+        assertEquals("jakarta.faces", profile.convert("javax.faces"));
+        assertEquals("jakarta.jms", profile.convert("javax.jms"));
         assertEquals("jakarta.json", profile.convert("javax.json"));
+        assertEquals("jakarta.jws", profile.convert("javax.jws"));
         assertEquals("jakarta.interceptor", profile.convert("javax.interceptor"));
         assertEquals("jakarta.inject", profile.convert("javax.inject"));
         assertEquals("jakarta.mail", profile.convert("javax.mail"));
+        assertEquals("jakarta.management.j2ee", profile.convert("javax.management.j2ee"));
         assertEquals("jakarta.persistence", profile.convert("javax.persistence"));
+        assertEquals("jakarta.resource", profile.convert("javax.resource"));
         assertEquals("jakarta.security.auth.message", profile.convert("javax.security.auth.message"));
+        assertEquals("jakarta.security.enterprise", profile.convert("javax.security.enterprise"));
+        assertEquals("jakarta.security.jacc", profile.convert("javax.security.jacc"));
         assertEquals("jakarta.servlet", profile.convert("javax.servlet"));
         assertEquals("jakarta.transaction", profile.convert("javax.transaction"));
         assertEquals("jakarta.validation", profile.convert("javax.validation"));
         assertEquals("jakarta.websocket", profile.convert("javax.websocket"));
         assertEquals("jakarta.ws.rs", profile.convert("javax.ws.rs"));
         assertEquals("jakarta.xml.bind", profile.convert("javax.xml.bind"));
-        assertEquals("jakarta.xml.namespace", profile.convert("javax.xml.namespace"));
+        assertEquals("jakarta.xml.registry", profile.convert("javax.xml.registry"));
         assertEquals("jakarta.xml.rpc", profile.convert("javax.xml.rpc"));
         assertEquals("jakarta.xml.soap", profile.convert("javax.xml.soap"));
-        assertEquals("jakarta.xml.stream", profile.convert("javax.xml.stream"));
         assertEquals("jakarta.xml.ws", profile.convert("javax.xml.ws"));
-        assertEquals("jakarta.xml.XMLConstants", profile.convert("javax.xml.XMLConstants"));
 
         // non EE javax packages
         assertEquals("javax.annotation.processing", profile.convert("javax.annotation.processing"));
+        assertEquals("javax.management", profile.convert("javax.management"));
         assertEquals("javax.security", profile.convert("javax.security"));
         assertEquals("javax.security.auth", profile.convert("javax.security.auth"));
         assertEquals("javax.swing", profile.convert("javax.swing"));
         assertEquals("javax.transaction.xa", profile.convert("javax.transaction.xa"));
+        assertEquals("javax.xml.stream", profile.convert("javax.xml.stream"));
+        assertEquals("javax.xml.namespace", profile.convert("javax.xml.namespace"));
+        assertEquals("javax.xml.xpath.XPathConstants", profile.convert("javax.xml.xpath.XPathConstants"));
+        assertEquals("javax.xml.XMLConstants", profile.convert("javax.xml.XMLConstants"));
     }
 }


=====================================
src/test/java/org/apache/tomcat/jakartaee/MigrationTest.java
=====================================
@@ -115,9 +115,8 @@ public class MigrationTest {
         Migration migration = new Migration();
         migration.setSource(sourceDirectory);
         migration.setDestination(destinationDirectory);
-        boolean success = migration.execute();
+        migration.execute();
 
-        assertTrue("Migration failed", success);
         assertTrue("Destination directory not found", destinationDirectory.exists());
 
         File migratedFile = new File("target/test-classes/migration/HelloServlet.java");


=====================================
src/test/java/org/apache/tomcat/jakartaee/PassThroughConverterTest.java
=====================================
@@ -26,6 +26,8 @@ import static org.junit.Assert.*;
 
 public class PassThroughConverterTest {
 
+    private static final String TEST_FILENAME = "project.properties";
+
     @Test
     public void testConverter() throws Exception {
         String content = "javax.servlet";
@@ -35,9 +37,9 @@ public class PassThroughConverterTest {
 
         Converter converter = new PassThroughConverter();
 
-        assertTrue(converter.accepts("project.properties"));
+        assertTrue(converter.accepts(TEST_FILENAME));
 
-        converter.convert(in, out, null);
+        converter.convert(TEST_FILENAME, in, out, null);
 
         assertArrayEquals(content.getBytes(), out.toByteArray());
     }


=====================================
src/test/java/org/apache/tomcat/jakartaee/TextConverterTest.java
=====================================
@@ -10,6 +10,8 @@ import org.junit.Test;
 
 public class TextConverterTest {
 
+    private static final String TEST_FILENAME = "text.txt";
+
 	private static final String INPUT = "javax.servlet.http.HttpServletRequest";
 	private static final String OUTPUT = "jakarta.servlet.http.HttpServletRequest";
 
@@ -23,7 +25,7 @@ public class TextConverterTest {
 		EESpecProfile profile = EESpecProfile.EE;
 
 		// test
-		converter.convert(in, out, profile);
+		converter.convert(TEST_FILENAME, in, out, profile);
 
 		// assert
 		String result = new String(out.toByteArray(), StandardCharsets.ISO_8859_1);



View it on GitLab: https://salsa.debian.org/java-team/tomcat-jakartaee-migration/-/compare/c4ac1c4a876732702399f45a794b27603b089baf...865db0d4bf871c89b31bc338b2d7316698b855de

-- 
View it on GitLab: https://salsa.debian.org/java-team/tomcat-jakartaee-migration/-/compare/c4ac1c4a876732702399f45a794b27603b089baf...865db0d4bf871c89b31bc338b2d7316698b855de
You're receiving this email because of your account on salsa.debian.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-java-commits/attachments/20210228/167879cf/attachment.htm>


More information about the pkg-java-commits mailing list