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 fb3091b Improve processing of relevant version numbers in manifests
fb3091b is described below
commit fb3091bd9e7ad520c4f94631a7b3c889d40fd8d4
Author: remm <remm@meteor>
AuthorDate: Fri Sep 4 15:07:35 2026 +0200
Improve processing of relevant version numbers in manifests
Co authored with OpenCode.
---
CHANGES.md | 1 +
.../apache/tomcat/jakartaee/ManifestConverter.java | 97 +++++++++++++++++-----
2 files changed, 79 insertions(+), 19 deletions(-)
diff --git a/CHANGES.md b/CHANGES.md
index 950ac5b..43aff26 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -8,6 +8,7 @@
- Do not buffer very large nested archives when processing them in streaming
mode. (remm)
- Improve cache robustness. (remm)
- Improve robustness of source and destination manipulation operations when
migrating. (remm)
+- Improve processing of relevant version numbers in manifests. (remm)
## 1.0.12
- Add Maven Wrapper Plugin to manage the Maven wrapper. (markt)
diff --git a/src/main/java/org/apache/tomcat/jakartaee/ManifestConverter.java
b/src/main/java/org/apache/tomcat/jakartaee/ManifestConverter.java
index aadb9b0..a42a1b8 100644
--- a/src/main/java/org/apache/tomcat/jakartaee/ManifestConverter.java
+++ b/src/main/java/org/apache/tomcat/jakartaee/ManifestConverter.java
@@ -38,21 +38,51 @@ import org.osgi.framework.BundleException;
import org.osgi.framework.Constants;
/**
- * Updates Manifests.
+ * Converter for JAR manifest files. Performs the following transformations:
+ * <ul>
+ * <li>Converts javax.* package names to jakarta.* in manifest attribute
values</li>
+ * <li>Updates OSGi Import-Package and Export-Package version constraints
+ * for jakarta.servlet packages</li>
+ * <li>Removes cryptographic signature attributes that are no longer
valid</li>
+ * <li>Appends a migration tool version suffix to Implementation-Version
attributes</li>
+ * </ul>
*/
public class ManifestConverter implements Converter {
- private static final String JAKARTA_SERVLET = "jakarta.servlet";
- private static final Pattern SERVLET_PATTERN =
Pattern.compile("jakarta.servlet([^,]*);version=\"(.*?)\"");
private static final Logger logger =
Logger.getLogger(ManifestConverter.class.getCanonicalName());
private static final StringManager sm =
StringManager.getManager(ManifestConverter.class);
+ private static final String JAKARTA_SERVLET = "jakarta.servlet";
+ // Matches a jakarta.servlet (or jakarta.servlet.*) package name, but not a
+ // package that merely contains that string (e.g. com.foo.jakarta.servlet
or
+ // jakarta.servletX), followed by a version attribute.
+ private static final Pattern SERVLET_PATTERN = Pattern.compile(
+
"(?<![A-Za-z0-9_$.])jakarta\\.servlet(?![A-Za-z0-9_$])([^,]*);version=\"(.*?)\"");
+ private String servletExportVersion = "5.0.0";
+ private String servletImportVersion = "[5.0.0,7.0.0)";
+
/**
* Manifest converter constructor.
*/
public ManifestConverter() {
}
+ /**
+ * Set the OSGi version used for jakarta.servlet Export-Package headers.
+ * @param servletExportVersion the export version (default: "5.0.0")
+ */
+ public void setServletExportVersion(String servletExportVersion) {
+ this.servletExportVersion = servletExportVersion;
+ }
+
+ /**
+ * Set the OSGi version range used for jakarta.servlet Import-Package
headers.
+ * @param servletImportVersion the import version range (default:
"[5.0.0,7.0.0)")
+ */
+ public void setServletImportVersion(String servletImportVersion) {
+ this.servletImportVersion = servletImportVersion;
+ }
+
@Override
public boolean accepts(String filename) {
if (filename.equals(JarFile.MANIFEST_NAME) || filename.endsWith("/" +
JarFile.MANIFEST_NAME)) {
@@ -66,7 +96,20 @@ public class ManifestConverter implements Converter {
@Override
public boolean convert(String path, InputStream src, OutputStream dest,
EESpecProfile profile) throws IOException {
byte[] srcBytes = IOUtils.toByteArray(src);
- Manifest srcManifest = new Manifest(new
ByteArrayInputStream(srcBytes));
+
+ Manifest srcManifest;
+ try {
+ srcManifest = new Manifest(new ByteArrayInputStream(srcBytes));
+ } catch (IOException e) {
+ // The manifest cannot be parsed (for example, a logical line
longer
+ // than the 8192 byte limit enforced by the JDK manifest parser) so
+ // it cannot be converted. Pass it through unchanged rather than
+ // failing the whole migration.
+ logger.log(Level.WARNING,
sm.getString("manifestConverter.manifestError", path), e);
+ IOUtils.writeChunked(srcBytes, dest);
+ return false;
+ }
+
Manifest destManifest = new Manifest(srcManifest);
// Only consider profile conversions, allowing Migration.hasConverted
to be true
@@ -132,7 +175,7 @@ public class ManifestConverter implements Converter {
attributes.put(Attributes.Name.IMPLEMENTATION_VERSION,
newValue);
logger.log(Level.FINE,
sm.getString("manifestConverter.updatedVersion", newValue));
}
- // Purposefully avoid setting result
+ // Purposefully avoid setting converted
}
// Update package names in values
for (Entry<Object, Object> entry : attributes.entrySet()) {
@@ -158,7 +201,7 @@ public class ManifestConverter implements Converter {
newValue = replaceVersion(newValue,
!Constants.EXPORT_PACKAGE.equals(header));
}
- // Object comparison is deliberate
+ // Value comparison to detect actual changes
if (!newValue.equals(entry.getValue())) {
entry.setValue(newValue);
converted = true;
@@ -168,11 +211,11 @@ public class ManifestConverter implements Converter {
}
private String processExportPackage(String value) throws BundleException {
- return processOSGIHeader(value, Constants.EXPORT_PACKAGE, "5.0.0");
+ return processOSGIHeader(value, Constants.EXPORT_PACKAGE,
servletExportVersion);
}
private String processImportPackage(String value) throws BundleException {
- return processOSGIHeader(value, Constants.IMPORT_PACKAGE,
"[5.0.0,7.0.0)");
+ return processOSGIHeader(value, Constants.IMPORT_PACKAGE,
servletImportVersion);
}
private String processOSGIHeader(String value, String header, String
replacement) throws BundleException {
@@ -180,25 +223,40 @@ public class ManifestConverter implements Converter {
ManifestElement[] elements = ManifestElement.parseHeader(header,
value);
boolean modified = false;
for (ManifestElement element : elements) {
- if (element.getValue().startsWith(JAKARTA_SERVLET)) {
+ String original = element.toString();
+ String result = original;
+ if (isJakartaServletPackage(element.getValue())) {
String oldVersion =
element.getAttribute(Constants.VERSION_ATTRIBUTE);
if (oldVersion != null) {
String escaped = Pattern.quote(oldVersion);
- String result =
element.toString().replaceFirst("(;version=\\\")" + escaped + "(\\\")",
- "$1" + replacement + "$2");
- packages.add(result);
- modified = true;
- } else {
- packages.add(element.toString());
+ result = original.replaceFirst("(;version=\\\")" + escaped
+ "(\\\")",
+ "$1" + Matcher.quoteReplacement(replacement) +
"$2");
}
+ }
+ if (result.equals(original)) {
+ packages.add(original);
} else {
- packages.add(element.toString());
+ packages.add(result);
+ modified = true;
}
}
if (!modified) {
return value;
}
- return String.join(",", packages);
+ return String.join(", ", packages);
+ }
+
+ /**
+ * Determines whether the given package name is the jakarta.servlet package
+ * or a sub-package of it (e.g. jakarta.servlet.http). Package names that
+ * merely contain that string (e.g. jakarta.servletX or
+ * com.foo.jakarta.servlet) are not matched.
+ * @param packageName the package name to test
+ * @return true if the package name is jakarta.servlet or a sub-package of
it
+ */
+ private static boolean isJakartaServletPackage(String packageName) {
+ return packageName != null &&
+ (packageName.equals(JAKARTA_SERVLET) ||
packageName.startsWith(JAKARTA_SERVLET + "."));
}
private String replaceVersion(String entryValue) {
@@ -210,8 +268,9 @@ public class ManifestConverter implements Converter {
StringBuffer builder = new StringBuffer();
Matcher matcher = SERVLET_PATTERN.matcher(entryValue);
while (matcher.find()) {
- String version = range ? "[5.0.0,7.0.0)" : "5.0.0";
- matcher.appendReplacement(builder,
"jakarta.servlet$1;version=\"" + version + "\"");
+ String version = range ? servletImportVersion :
servletExportVersion;
+ matcher.appendReplacement(builder,
+ "jakarta.servlet$1;version=\"" +
Matcher.quoteReplacement(version) + "\"");
}
matcher.appendTail(builder);
return builder.toString();
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]