This is an automated email from the ASF dual-hosted git repository.

rmaucher pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tomcat-jakartaee-migration.git


The following commit(s) were added to refs/heads/main by this push:
     new 1b34925  Avoid unwanted mutation of source attributes when converting
1b34925 is described below

commit 1b3492586743e9d9ef5857d68e531ef1aba2499a
Author: remm <remm@meteor>
AuthorDate: Mon Sep 7 11:36:53 2026 +0200

    Avoid unwanted mutation of source attributes when converting
    
    Add additional tests for manifest conversion.
    Co authored with OpenCode.
---
 CHANGES.md                                         |   3 +-
 .../apache/tomcat/jakartaee/ManifestConverter.java |   7 +
 .../tomcat/jakartaee/ManifestConverterTest.java    | 148 +++++++++++++++++++++
 3 files changed, 157 insertions(+), 1 deletion(-)

diff --git a/CHANGES.md b/CHANGES.md
index 43aff26..a258506 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -9,11 +9,12 @@
 - Improve cache robustness. (remm)
 - Improve robustness of source and destination manipulation operations when 
migrating. (remm)
 - Improve processing of relevant version numbers in manifests. (remm)
+- Avoid shallow copy style issue when converting manifests attributes. (remm)
 
 ## 1.0.12
 - Add Maven Wrapper Plugin to manage the Maven wrapper. (markt)
 - Update the Maven Wrapper and switch to 'only-script' configuration. (markt)
-- Enable successful running of tests without having to clean the output 
directory bewteen test runs. (markt)
+- Enable successful running of tests without having to clean the output 
directory between test runs. (markt)
 
 ## 1.0.11
 - Update Eclipse OSGI to 3.23.200. (dependabot/remm)
diff --git a/src/main/java/org/apache/tomcat/jakartaee/ManifestConverter.java 
b/src/main/java/org/apache/tomcat/jakartaee/ManifestConverter.java
index cc33e84..ac31888 100644
--- a/src/main/java/org/apache/tomcat/jakartaee/ManifestConverter.java
+++ b/src/main/java/org/apache/tomcat/jakartaee/ManifestConverter.java
@@ -120,7 +120,14 @@ public class ManifestConverter implements Converter {
             return false;
         }
 
+        // The JDK Manifest copy constructor shares the section attributes with
+        // the source manifest, so changes to sections made below would also be
+        // visible in the source and be lost when the (equal) source bytes are
+        // written back unchanged. Replace the sections with copies.
         Manifest destManifest = new Manifest(srcManifest);
+        for (Entry<String, Attributes> entry : 
srcManifest.getEntries().entrySet()) {
+            destManifest.getEntries().put(entry.getKey(), new 
Attributes(entry.getValue()));
+        }
 
         // Only consider profile conversions, allowing Migration.hasConverted 
to be true
         // only when there are actual
diff --git 
a/src/test/java/org/apache/tomcat/jakartaee/ManifestConverterTest.java 
b/src/test/java/org/apache/tomcat/jakartaee/ManifestConverterTest.java
index 79a9d82..6b5438c 100644
--- a/src/test/java/org/apache/tomcat/jakartaee/ManifestConverterTest.java
+++ b/src/test/java/org/apache/tomcat/jakartaee/ManifestConverterTest.java
@@ -224,4 +224,152 @@ public class ManifestConverterTest {
 
         assertFalse("Should not convert manifest with no javax packages", 
converted);
     }
+
+    @Test
+    public void testReplaceVersionInNonOSGiHeader() throws IOException {
+        ManifestConverter converter = new ManifestConverter();
+
+        // A header that is not Import-Package/Export-Package: the version of
+        // the jakarta.servlet package should be replaced with the import
+        // version range, other packages should be left alone
+        Manifest manifest = new Manifest();
+        manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, 
"1.0");
+        manifest.getMainAttributes().putValue("Custom-Header",
+                
"javax.servlet;version=\"4.0.0\",javax.sql.DataSource;version=\"2.0.0\"");
+
+        ByteArrayOutputStream manifestBytes = new ByteArrayOutputStream();
+        manifest.write(manifestBytes);
+
+        ByteArrayOutputStream dest = new ByteArrayOutputStream();
+        boolean converted = converter.convert("META-INF/MANIFEST.MF",
+                new ByteArrayInputStream(manifestBytes.toByteArray()), dest, 
EESpecProfiles.TOMCAT);
+
+        assertTrue("Version replacement should count as a conversion", 
converted);
+        String result = dest.toString("UTF-8").replaceAll("\\s", "");
+        assertTrue(result, result.contains(
+                
"jakarta.servlet;version=\"[5.0.0,7.0.0)\",javax.sql.DataSource;version=\"2.0.0\""));
+    }
+
+    @Test
+    public void testReplaceVersionUsesConfiguredVersion() throws IOException {
+        ManifestConverter converter = new ManifestConverter();
+        converter.setServletImportVersion("[6.0.0,8.0.0)");
+
+        Manifest manifest = new Manifest();
+        manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, 
"1.0");
+        manifest.getMainAttributes().putValue("Custom-Header",
+                "jakarta.servlet.http;version=\"4.0.0\"");
+
+        ByteArrayOutputStream manifestBytes = new ByteArrayOutputStream();
+        manifest.write(manifestBytes);
+
+        ByteArrayOutputStream dest = new ByteArrayOutputStream();
+        boolean converted = converter.convert("META-INF/MANIFEST.MF",
+                new ByteArrayInputStream(manifestBytes.toByteArray()), dest, 
EESpecProfiles.TOMCAT);
+
+        assertTrue("Version replacement should count as a conversion", 
converted);
+        String result = dest.toString("UTF-8").replaceAll("\\s", "");
+        assertTrue(result,
+                
result.contains("jakarta.servlet.http;version=\"[6.0.0,8.0.0)\""));
+    }
+
+    @Test
+    public void testReplaceVersionInSectionAttributes() throws IOException {
+        ManifestConverter converter = new ManifestConverter();
+
+        Manifest manifest = new Manifest();
+        manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, 
"1.0");
+        Attributes section = new Attributes();
+        section.putValue("Custom-Header", "javax.servlet;version=\"4.0.0\"");
+        manifest.getEntries().put("test.txt", section);
+
+        ByteArrayOutputStream manifestBytes = new ByteArrayOutputStream();
+        manifest.write(manifestBytes);
+
+        ByteArrayOutputStream dest = new ByteArrayOutputStream();
+        boolean converted = converter.convert("META-INF/MANIFEST.MF",
+                new ByteArrayInputStream(manifestBytes.toByteArray()), dest, 
EESpecProfiles.TOMCAT);
+
+        assertTrue("Version replacement should count as a conversion", 
converted);
+        String result = dest.toString("UTF-8").replaceAll("\\s", "");
+        assertTrue(result,
+                result.contains("jakarta.servlet;version=\"[5.0.0,7.0.0)\""));
+    }
+
+    @Test
+    public void testReplaceVersionFallbackOnInvalidImportPackage() throws 
IOException {
+        ManifestConverter converter = new ManifestConverter();
+
+        // Malformed header: parsing fails so the fallback version replacement
+        // applies the import version range
+        Manifest manifest = new Manifest();
+        manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, 
"1.0");
+        manifest.getMainAttributes().putValue("Import-Package",
+                "javax.servlet;uses:=\"x\"(;version=\"4.0.0\"");
+
+        ByteArrayOutputStream manifestBytes = new ByteArrayOutputStream();
+        manifest.write(manifestBytes);
+
+        ByteArrayOutputStream dest = new ByteArrayOutputStream();
+        boolean converted = converter.convert("META-INF/MANIFEST.MF",
+                new ByteArrayInputStream(manifestBytes.toByteArray()), dest, 
EESpecProfiles.TOMCAT);
+
+        assertTrue("Version replacement should count as a conversion", 
converted);
+        String result = dest.toString("UTF-8").replaceAll("\\s", "");
+        assertTrue(result, result.contains(
+                "jakarta.servlet;uses:=\"x\"(;version=\"[5.0.0,7.0.0)\""));
+    }
+
+    @Test
+    public void testReplaceVersionFallbackOnInvalidExportPackage() throws 
IOException {
+        ManifestConverter converter = new ManifestConverter();
+
+        // Malformed Export-Package header: parsing fails so the fallback
+        // version replacement applies the single export version (Export-
+        // Package cannot use a version range)
+        Manifest manifest = new Manifest();
+        manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, 
"1.0");
+        manifest.getMainAttributes().putValue("Export-Package",
+                "javax.servlet;uses:=\"x\"(;version=\"4.0.0\"");
+
+        ByteArrayOutputStream manifestBytes = new ByteArrayOutputStream();
+        manifest.write(manifestBytes);
+
+        ByteArrayOutputStream dest = new ByteArrayOutputStream();
+        boolean converted = converter.convert("META-INF/MANIFEST.MF",
+                new ByteArrayInputStream(manifestBytes.toByteArray()), dest, 
EESpecProfiles.TOMCAT);
+
+        assertTrue("Version replacement should count as a conversion", 
converted);
+        String result = dest.toString("UTF-8").replaceAll("\\s", "");
+        assertTrue(result, result.contains(
+                "jakarta.servlet;uses:=\"x\"(;version=\"5.0.0\""));
+    }
+
+    @Test
+    public void testReplaceVersionIgnoresLookalikePackages() throws 
IOException {
+        ManifestConverter converter = new ManifestConverter();
+
+        // Values that contain the jakarta.servlet string but do not match the
+        // servlet package version pattern must be left unchanged
+        Manifest manifest = new Manifest();
+        manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, 
"1.0");
+        manifest.getMainAttributes().putValue("Custom-Header",
+                "com.example.jakarta.servlet;version=\"1.0.0\", " +
+                "jakarta.servletX;version=\"1.0.0\", " +
+                "jakarta.servlet");
+
+        ByteArrayOutputStream manifestBytes = new ByteArrayOutputStream();
+        manifest.write(manifestBytes);
+
+        ByteArrayOutputStream dest = new ByteArrayOutputStream();
+        boolean converted = converter.convert("META-INF/MANIFEST.MF",
+                new ByteArrayInputStream(manifestBytes.toByteArray()), dest, 
EESpecProfiles.TOMCAT);
+
+        assertFalse("Similar package names should not be modified", converted);
+        String result = dest.toString("UTF-8").replaceAll("\\s", "");
+        assertTrue(result, result.contains(
+                "com.example.jakarta.servlet;version=\"1.0.0\"," +
+                "jakarta.servletX;version=\"1.0.0\"," +
+                "jakarta.servlet"));
+    }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to