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]