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

markt 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 7bdcfbc  Correctly handle OSGi headers. PR[#54] by Kyle Smith.
7bdcfbc is described below

commit 7bdcfbc605aa5e912be1c7705c12a4a7392217ab
Author: Mark Thomas <[email protected]>
AuthorDate: Wed Nov 12 16:48:48 2025 +0000

    Correctly handle OSGi headers. PR[#54] by Kyle Smith.
---
 CHANGES.md                                         |  1 +
 pom.xml                                            | 13 ++++
 .../apache/tomcat/jakartaee/ManifestConverter.java | 77 ++++++++++++++++++----
 .../tomcat/jakartaee/ManifestConverterTest.java    | 40 ++++++++++-
 src/test/resources/MANIFEST.test.MF                |  9 +++
 5 files changed, 127 insertions(+), 13 deletions(-)

diff --git a/CHANGES.md b/CHANGES.md
index 625475c..72aeb6b 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -3,6 +3,7 @@
 ## 1.0.10
 - When migrating files in place, don't replace the original file if no 
conversion has taken place. Based on PR[#78] by Semiao Marco.
 - When converting a file in an archive, update the last modified time for that 
archive entry.  Based on PR[#78] by Semiao Marco.
+- Correctly handle OSGi headers. PR[#54] by Kyle Smith.
 - Update ASF parent POM 34. (dependabot/markt)
 - Update Commons BCEL to 6.11.0. (dependabot/remm)
 - Update Commons Compress to 1.28.0. (dependabot/remm)
diff --git a/pom.xml b/pom.xml
index 1e3ed27..8fa872d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -95,6 +95,11 @@
       <version>1.10.15</version>
       <scope>provided</scope>
     </dependency>
+    <dependency>
+        <groupId>org.eclipse.platform</groupId>
+        <artifactId>org.eclipse.osgi</artifactId>
+        <version>3.18.600</version>
+    </dependency>
 
     <!-- Test dependencies -->
     <dependency>
@@ -103,6 +108,7 @@
       <version>4.13.2</version>
       <scope>test</scope>
     </dependency>
+    
   </dependencies>
 
   <profiles>
@@ -282,6 +288,13 @@
                     <exclude>META-INF/**</exclude>
                   </excludes>
                 </filter>
+                <filter>
+                  <artifact>org.eclipse.platform:*</artifact>
+                  <excludes>
+                    <exclude>META-INF/**</exclude>
+                    <exclude>module-info.class</exclude>
+                  </excludes>
+                </filter>
               </filters>
               <relocations>
                 <relocation>
diff --git a/src/main/java/org/apache/tomcat/jakartaee/ManifestConverter.java 
b/src/main/java/org/apache/tomcat/jakartaee/ManifestConverter.java
index 63aef73..cc93965 100644
--- a/src/main/java/org/apache/tomcat/jakartaee/ManifestConverter.java
+++ b/src/main/java/org/apache/tomcat/jakartaee/ManifestConverter.java
@@ -33,19 +33,25 @@ import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 import org.apache.commons.io.IOUtils;
+import org.eclipse.osgi.util.ManifestElement;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
 
 /**
  * Updates Manifests.
  */
 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);
 
     /**
      * Manifest converter constructor.
      */
-    public ManifestConverter() {}
+    public ManifestConverter() {
+    }
 
     @Override
     public boolean accepts(String filename) {
@@ -63,7 +69,8 @@ public class ManifestConverter implements Converter {
         Manifest srcManifest = new Manifest(new 
ByteArrayInputStream(srcBytes));
         Manifest destManifest = new Manifest(srcManifest);
 
-        // Only consider profile conversions, allowing Migration.hasConverted 
to be true only when there are actual
+        // Only consider profile conversions, allowing Migration.hasConverted 
to be true
+        // only when there are actual
         // conversions made
         boolean converted = updateValues(destManifest, profile);
         removeSignatures(destManifest);
@@ -80,7 +87,6 @@ public class ManifestConverter implements Converter {
         return converted;
     }
 
-
     private void removeSignatures(Manifest manifest) {
         manifest.getMainAttributes().remove(Attributes.Name.SIGNATURE_VERSION);
         List<String> signatureEntries = new ArrayList<>();
@@ -98,7 +104,6 @@ public class ManifestConverter implements Converter {
         }
     }
 
-
     private boolean isCryptoSignatureEntry(Attributes attributes) {
         for (Object attributeKey : attributes.keySet()) {
             if (attributeKey.toString().endsWith("-Digest")) {
@@ -108,7 +113,6 @@ public class ManifestConverter implements Converter {
         return false;
     }
 
-
     private boolean updateValues(Manifest manifest, EESpecProfile profile) {
         boolean converted = updateValues(manifest.getMainAttributes(), 
profile);
         for (Attributes attributes : manifest.getEntries().values()) {
@@ -117,7 +121,6 @@ public class ManifestConverter implements Converter {
         return converted;
     }
 
-
     private boolean updateValues(Attributes attributes, EESpecProfile profile) 
{
         boolean converted = false;
         // Update version info
@@ -128,11 +131,27 @@ public class ManifestConverter implements Converter {
             // Purposefully avoid setting result
         }
         // Update package names in values
-        for (Entry<Object,Object> entry : attributes.entrySet()) {
+        for (Entry<Object, Object> entry : attributes.entrySet()) {
             String newValue = profile.convert((String) entry.getValue());
-            newValue = replaceVersion(newValue);
+            String header = entry.getKey().toString();
+            try {
+                // Need to be careful with OSGI headers.
+                // Specifically, Export-Package cannot specify a version range.
+                // There may be other weird things as well (like directives 
that have
+                // jakarta.servlet packages).
+                if (Constants.IMPORT_PACKAGE.equals(header)) {
+                    newValue = processImportPackage(newValue);
+                } else if (Constants.EXPORT_PACKAGE.equals(header)) {
+                    newValue = processExportPackage(newValue);
+                } else {
+                    newValue = replaceVersion(newValue);
+                }
+            } catch (BundleException e) {
+                newValue = replaceVersion(newValue, 
!Constants.EXPORT_PACKAGE.equals(header));
+            }
+
             // Object comparison is deliberate
-            if (newValue != entry.getValue()) {
+            if (!newValue.equals(entry.getValue())) {
                 entry.setValue(newValue);
                 converted = true;
             }
@@ -140,12 +159,46 @@ public class ManifestConverter implements Converter {
         return converted;
     }
 
+    private String processExportPackage(String value) throws BundleException {
+        return processOSGIHeader(value, Constants.EXPORT_PACKAGE, "5.0.0");
+    }
+
+    private String processImportPackage(String value) throws BundleException {
+        return processOSGIHeader(value, Constants.IMPORT_PACKAGE, 
"[5.0.0,7.0.0)");
+    }
+
+    private String processOSGIHeader(String value, String header, String 
replacement) throws BundleException {
+        List<String> packages = new ArrayList<>();
+        ManifestElement[] elements = ManifestElement.parseHeader(header, 
value);
+        for (ManifestElement element : elements) {
+            if (element.getValue().startsWith(JAKARTA_SERVLET)) {
+                String oldVersion = 
element.getAttribute(Constants.VERSION_ATTRIBUTE);
+                if (oldVersion != null) {
+                    packages.add(element.toString().replace(oldVersion, 
replacement));
+                } else {
+                    packages.add(element.toString());
+                }
+            } else {
+                packages.add(element.toString());
+            }
+        }
+        if (packages.isEmpty()) {
+            return value;
+        }
+        return String.join(",", packages);
+    }
+
     private String replaceVersion(String entryValue) {
-        if (entryValue.contains("jakarta.servlet")) {
+        return replaceVersion(entryValue, true);
+    }
+
+    private String replaceVersion(String entryValue, boolean range) {
+        if (entryValue.contains(JAKARTA_SERVLET)) {
             StringBuffer builder = new StringBuffer();
-            Matcher matcher = 
Pattern.compile("jakarta.servlet([^,]*);version=\"(.*?)\"").matcher(entryValue);
+            Matcher matcher = SERVLET_PATTERN.matcher(entryValue);
             while (matcher.find()) {
-                matcher.appendReplacement(builder, 
"jakarta.servlet$1;version=\"[5.0.0,7.0.0)\"");
+                String version = range ? "[5.0.0,7.0.0)" : "5.0.0";
+                matcher.appendReplacement(builder, 
"jakarta.servlet$1;version=\"" + version + "\"");
             }
             matcher.appendTail(builder);
             return builder.toString();
diff --git 
a/src/test/java/org/apache/tomcat/jakartaee/ManifestConverterTest.java 
b/src/test/java/org/apache/tomcat/jakartaee/ManifestConverterTest.java
index 6748dba..1775537 100644
--- a/src/test/java/org/apache/tomcat/jakartaee/ManifestConverterTest.java
+++ b/src/test/java/org/apache/tomcat/jakartaee/ManifestConverterTest.java
@@ -14,12 +14,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package org.apache.tomcat.jakartaee;
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
 import org.junit.Test;
 
 public class ManifestConverterTest {
@@ -34,4 +35,41 @@ public class ManifestConverterTest {
         assertFalse(converter.accepts("xMETA-INF/MANIFEST.MF"));
         
assertFalse(converter.accepts("WEB-INF/bundles/com.example.bundle/xMETA-INF/MANIFEST.MF"));
     }
+
+
+    @Test
+    public void testConvert() throws IOException {
+        ManifestConverter converter = new ManifestConverter();
+        ByteArrayOutputStream os = new ByteArrayOutputStream();
+        boolean converted = converter.convert("/MANIFEST.test.MF", 
getClass().getResourceAsStream("/MANIFEST.test.MF"),
+                os, EESpecProfiles.TOMCAT);
+        assertTrue(converted);
+
+        String result = os.toString("UTF-8");
+        System.out.println(result);
+        assertTrue(result.length() != 0);
+        result = result.replaceAll("\\s", "");
+
+        // Basic test
+        String imports = "jakarta.servlet;version=\"[5.0.0,7.0.0)\"";
+
+        // Test with directives
+        String imports2 = 
"jakarta.servlet.http;version=\"[5.0.0,7.0.0)\";resolution:=\"optional\"";
+        assertTrue(result.contains(imports));
+        assertTrue(result.contains(imports2));
+
+        // Test with directive and version
+        String exports = 
"jakarta.servlet;version=\"5.0.0\";uses:=\"org.eclipse.core.runtime\"";
+
+        // Same as above, with javax.servlet package in the directive
+        String exports2 = 
"jakarta.servlet.http;version=\"5.0.0\";uses:=\"jakarta.servlet\"";
+
+        // Export a different package that has javax.servlet in a directive so 
version
+        // isn't updated
+        String exports3 = 
"org.apache.tomcat.jakartaee.test;version=\"1.0.0\";uses:=\"jakarta.servlet\"";
+
+        assertTrue(result.contains(exports));
+        assertTrue(result.contains(exports2));
+        assertTrue(result.contains(exports3));
+    }
 }
diff --git a/src/test/resources/MANIFEST.test.MF 
b/src/test/resources/MANIFEST.test.MF
new file mode 100644
index 0000000..06cbcfb
--- /dev/null
+++ b/src/test/resources/MANIFEST.test.MF
@@ -0,0 +1,9 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Version: 1.0.0.qualifier
+Bundle-SymbolicName: org.apache.tomcat.jakartaee.test
+Import-Package: javax.servlet;version="[2.0.0,5.0.0)",
+ javax.servlet.http;resolution:=optional;version="[2.0.0,5.0.0)"
+Export-Package: javax.servlet;uses:="org.eclipse.core.runtime";version="4.0.0",
+ javax.servlet.http;uses:="javax.servlet";version="4.0.0",
+ org.apache.tomcat.jakartaee.test;uses:="javax.servlet";version="1.0.0"


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

Reply via email to