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

nfilotto pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-karaf.git


The following commit(s) were added to refs/heads/main by this push:
     new b523e94cf Ref #287: Add camel-karaf-feature-maven-plugin (#317)
b523e94cf is described below

commit b523e94cfe0c2fe3f23de1e199f4a384b7fbd598
Author: Stefan Tataru <[email protected]>
AuthorDate: Tue Jun 4 15:48:47 2024 +0200

    Ref #287: Add camel-karaf-feature-maven-plugin (#317)
    
    Maven Plugin that modifies `karaf/features/target/feature/camel-features.xm`
    
    `ensure-wrap-bundle-version` - will add the **Bundle-Version** header to 
the bundles that are using the **wrap** protocol and do not already have the 
header declared. It will also check the existing Bundle-Version declarations 
and make sure that they are OSGI compliant.
    
    ---------
    
    Co-authored-by: Nicolas Filotto <[email protected]>
---
 features/pom.xml                                   |  15 ++
 features/src/main/feature/camel-features.xml       |   2 +-
 pom.xml                                            |   2 +
 tooling/camel-karaf-feature-maven-plugin/pom.xml   | 104 ++++++++
 .../feature/maven/EnsureWrapBundleVersionMojo.java | 270 +++++++++++++++++++++
 .../maven/EnsureWrapBundleVersionMojoTest.java     | 143 +++++++++++
 tooling/pom.xml                                    |  35 +++
 7 files changed, 570 insertions(+), 1 deletion(-)

diff --git a/features/pom.xml b/features/pom.xml
index 7866f42d6..892315938 100644
--- a/features/pom.xml
+++ b/features/pom.xml
@@ -63,6 +63,21 @@
                     </execution>
                 </executions>
             </plugin>
+            <plugin>
+                <groupId>org.apache.camel.karaf</groupId>
+                <artifactId>camel-karaf-feature-maven-plugin</artifactId>
+                <version>${project.version}</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>ensure-wrap-bundle-version</goal>
+                        </goals>
+                        <configuration>
+                            
<featuresFilePath>file:${project.build.directory}/feature/camel-features.xml</featuresFilePath>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
             <plugin>
                 <groupId>org.apache.karaf.tooling</groupId>
                 <artifactId>karaf-maven-plugin</artifactId>
diff --git a/features/src/main/feature/camel-features.xml 
b/features/src/main/feature/camel-features.xml
index 52d31f837..cbd4828d2 100644
--- a/features/src/main/feature/camel-features.xml
+++ b/features/src/main/feature/camel-features.xml
@@ -17,7 +17,7 @@
     limitations under the License.
 
 -->
-<features xmlns="http://karaf.apache.org/xmlns/features/v1.5.0"; 
name="camel-${project.version}">
+<features xmlns="http://karaf.apache.org/xmlns/features/v1.6.0"; 
name="camel-${project.version}">
 
     <!-- TODO: specs will be provided by Apache Karaf 4.5.x spec features 
repository. However, in order to have Camel 4.x working on Karaf 4.4.x, we 
define spec features here -->
     <feature name="jakarta-activation" version="${jakarta-activation-version}">
diff --git a/pom.xml b/pom.xml
index e6c2a9deb..ea0aa6f16 100644
--- a/pom.xml
+++ b/pom.xml
@@ -70,6 +70,7 @@
         <!-- <module>archetypes</module> -->
         <!-- <module>bom</module> -->
         <module>tests</module>
+        <module>tooling</module>
     </modules>
 
     <scm>
@@ -548,6 +549,7 @@
         <osgi.service.event.version>1.4.1</osgi.service.event.version>
         <osgi.service.component.version>1.5.1</osgi.service.component.version>
         <slf4j.version>2.0.13</slf4j.version>
+        <servicemix-specs-version>10</servicemix-specs-version> 
 
         <camel.osgi.version.clean>4.6</camel.osgi.version.clean>
         <camel.osgi.next.version.clean>4.7</camel.osgi.next.version.clean>
diff --git a/tooling/camel-karaf-feature-maven-plugin/pom.xml 
b/tooling/camel-karaf-feature-maven-plugin/pom.xml
new file mode 100644
index 000000000..6a039ecdd
--- /dev/null
+++ b/tooling/camel-karaf-feature-maven-plugin/pom.xml
@@ -0,0 +1,104 @@
+<!--
+    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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
https://maven.apache.org/xsd/maven-4.0.0.xsd";>
+    <modelVersion>4.0.0</modelVersion>
+    
+    <parent>
+        <groupId>org.apache.camel.karaf</groupId>
+        <artifactId>tooling</artifactId>
+        <version>4.6.0-SNAPSHOT</version>
+    </parent>
+    
+    <artifactId>camel-karaf-feature-maven-plugin</artifactId>
+    <packaging>maven-plugin</packaging>
+    <name>Apache Camel :: Karaf :: Tooling :: Feature Maven Plugin</name>
+
+    <properties>
+        <felix.utils.version>1.11.8</felix.utils.version>
+        <maven.version>3.9.7</maven.version>
+    </properties>
+    
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.karaf.features</groupId>
+            <artifactId>org.apache.karaf.features.core</artifactId>
+            <version>${karaf.version}</version>
+        </dependency>
+         
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>osgi.core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.utils</artifactId>
+            <version>${felix.utils.version}</version>
+        </dependency>
+
+        <!-- maven plugin -->
+        <dependency>
+            <groupId>org.apache.maven</groupId>
+            <artifactId>maven-plugin-api</artifactId>
+            <version>${maven.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.maven.plugin-tools</groupId>
+            <artifactId>maven-plugin-annotations</artifactId>
+        </dependency>
+
+        <!-- tests -->
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-api</artifactId>
+            <version>${junit-jupiter-version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-engine</artifactId>
+            <version>${junit-jupiter-version}</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+    
+    <build>
+        <plugins>
+            <!-- maven plugin -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-plugin-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>default-descriptor</id>
+                        <goals>
+                            <goal>descriptor</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            
+            <!-- tests -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+    
+</project>
\ No newline at end of file
diff --git 
a/tooling/camel-karaf-feature-maven-plugin/src/main/java/org/apache/camel/karaf/feature/maven/EnsureWrapBundleVersionMojo.java
 
b/tooling/camel-karaf-feature-maven-plugin/src/main/java/org/apache/camel/karaf/feature/maven/EnsureWrapBundleVersionMojo.java
new file mode 100644
index 000000000..ef06fe20d
--- /dev/null
+++ 
b/tooling/camel-karaf-feature-maven-plugin/src/main/java/org/apache/camel/karaf/feature/maven/EnsureWrapBundleVersionMojo.java
@@ -0,0 +1,270 @@
+/*
+ * 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.camel.karaf.feature.maven;
+
+import java.io.StringWriter;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.felix.utils.version.VersionCleaner;
+import org.apache.karaf.features.internal.model.Bundle;
+import org.apache.karaf.features.internal.model.Feature;
+import org.apache.karaf.features.internal.model.Features;
+import org.apache.karaf.features.internal.model.JaxbUtil;
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugins.annotations.LifecyclePhase;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.osgi.framework.Version;
+
+@Mojo(name = "ensure-wrap-bundle-version", defaultPhase = 
LifecyclePhase.PROCESS_RESOURCES)
+public class EnsureWrapBundleVersionMojo extends AbstractMojo {
+
+    private static final String FILE_PROTOCOL = "file:";
+
+    private static final String WRAP_PROTOCOL = "wrap:mvn:";
+    private static final List<String> HEADERS_AFTER_BUNDLE_VEIRSION = 
Arrays.asList(
+            //"Bundle-Version",
+            "DynamicImport-Package",
+            "Export-Package",
+            "Export-Service",
+            "Fragment-Host",
+            "Import-Package",
+            "Import-Service",
+            "Provide-Capability",
+            "Require-Bundle",
+            "Require-Capability");
+    
+    private static final String DEFAULT_HEADER = "<?xml version=\"1.0\" 
encoding=\"UTF-8\" standalone=\"yes\"?>";
+    private static final String LICENCE_HEADER = """
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->""";
+
+    static final String BUNDLE_VERSION = "Bundle-Version";
+
+    @Parameter(property = "featuresFilePath", required = true)
+    private String featuresFilePath;
+
+    public String getFeaturesFilePath() {
+        return featuresFilePath;
+    }
+
+    public void setFeaturesFilePath(String featuresFilePath) {
+        this.featuresFilePath = featuresFilePath;
+    }
+
+    @Override
+    public void execute() throws MojoExecutionException {
+        Features featuresData = JaxbUtil.unmarshal(getFeaturesFilePath(), 
false);
+        processFeatures(featuresData.getFeature());
+
+        marshal(featuresData);
+    }
+
+    private void marshal(Features featuresData) throws MojoExecutionException {
+        try (StringWriter writer = new StringWriter()) {
+            JaxbUtil.marshal(featuresData, writer);
+
+            String result = writer.toString().replace(DEFAULT_HEADER, 
LICENCE_HEADER);
+
+            Path path = 
Paths.get(getFeaturesFilePath().replaceFirst(FILE_PROTOCOL, ""));
+            Files.writeString(path, result);
+
+            getLog().info("File '%s' was successfully modified and 
saved".formatted(getFeaturesFilePath()));
+        } catch (Exception e) {
+            getLog().error("File '%s' was successfully modified but an error 
occurred while saving it"
+                    .formatted(getFeaturesFilePath()), e);
+            throw new MojoExecutionException(e);
+        }
+    }
+
+    private void processFeatures(List<Feature> features) {
+        for (Feature feature : features) {
+            processFeature(feature);
+        }
+    }
+
+    private void processFeature(Feature feature) {
+        for (Bundle bundle : feature.getBundle()) {
+            String location = bundle.getLocation();
+            if (location != null && location.startsWith(WRAP_PROTOCOL)) {
+                try {
+                    bundle.setLocation(processLocation(location));
+                } catch (Exception e) {
+                    getLog().error("Could not process the Bundle location 
'%s': %s".formatted(location, e.getMessage()), e);
+                }
+            }
+        }
+    }
+
+    String processLocation(String location) throws Exception {
+        int versionStartIndex = getVersionStartIndex(location);
+        int versionEndIndex = getVersionEndIndex(location, versionStartIndex);
+
+        String rawVersion = getVersion(location, versionStartIndex, 
versionEndIndex);
+        String version = getValidVersion(location, rawVersion);
+
+        String bundleVersionHeader = "%s=%s".formatted(BUNDLE_VERSION, 
version);
+
+        if (location.contains(bundleVersionHeader)) {
+            return location;
+        } else if (location.contains(BUNDLE_VERSION)) {
+            return updateExistingVersion(location, bundleVersionHeader);
+        }
+
+        String wrapProtocolOptions = location.substring(versionEndIndex + 1, 
location.length());
+        StringBuilder sb = new StringBuilder(location);
+
+        // insert before existing headers header
+        for (String header : HEADERS_AFTER_BUNDLE_VEIRSION) {
+            // add Bundle-Version before
+            if (location.contains(header)) {
+                int versionHeaderStartIndex = location.indexOf(header);
+                if (wrapProtocolOptions.contains("$")) {
+                    // "amp;" is automatically added
+                    return sb.insert(versionHeaderStartIndex, 
"%s&".formatted(bundleVersionHeader)).toString();
+                } else {
+                    // "amp;" is automatically added
+                    return sb.insert(versionHeaderStartIndex, 
"$%s&".formatted(bundleVersionHeader)).toString();
+                }
+            }
+        }
+
+        // insert at the end
+        if (wrapProtocolOptions.contains("$")) {
+            // "amp;" is automatically added
+            return sb.insert(location.length(), 
"&%s".formatted(bundleVersionHeader)).toString();
+        } else {
+            return sb.insert(location.length(), 
"$%s".formatted(bundleVersionHeader)).toString();
+        }
+    }
+
+    /**
+     * @return artifact version first char index, inclusive
+     */
+    int getVersionStartIndex(String location) {
+        boolean artifactIdFound = false;
+        for (int i = 0; i < location.length(); i++) {
+            if ('/' == location.charAt(i)) {
+                if (!artifactIdFound) {
+                    artifactIdFound = true;
+                } else {
+                    return i + 1;
+                }
+            }
+        }
+
+        return -1;
+    }
+
+    int getVersionEndIndex(String location) {
+        return getVersionEndIndex(location, getVersionStartIndex(location));
+    }
+
+    /**
+     * @return artifact version last char index, inclusive
+     */
+    int getVersionEndIndex(String location, int versionStartIndex) {
+        // start at + 1 to ignore the potential $ coming from version 
placeholder
+        for (int i = versionStartIndex + 1; i < location.length(); i++) {
+            if ('$' == location.charAt(i)) {
+                return i - 1;
+            }
+        }
+
+        return location.length() - 1;
+    }
+
+    String getVersion(String Location) {
+        return getVersion(Location, getVersionStartIndex(Location), 
getVersionEndIndex(Location));
+    }
+
+    String getVersion(String location, int versionStartIndex, int 
versionEndIndex) {
+        return location.substring(versionStartIndex, versionEndIndex + 1);
+    }
+
+    String getValidVersion(String location, String version) throws Exception {
+        if (version.charAt(0) == '$') {
+            throw new Exception("Maven version placeholder '%s' wasn't 
resolved".formatted(version));
+        }
+
+        String cleanVersion = VersionCleaner.clean(version);
+        if (getLog().isDebugEnabled()) {
+            getLog().debug(
+                    "Bundle location '%s' will be set with Bundle-Version 
'%s', the output of VersionCleaner.clean(%s)"
+                            .formatted(location, cleanVersion, version));
+        }
+
+        // test if clean version will work in Karaf, just in case
+        try {
+            new Version(cleanVersion);
+            return cleanVersion;
+        } catch (Exception newException) {
+            throw new Exception("Version '%s' is not OSGi 
compliant".formatted(cleanVersion), newException);
+        }
+    }
+
+    String updateExistingVersion(String location, String bundleVersioHeader) 
throws Exception {
+        int versionHeaderStartIndex = location.indexOf(BUNDLE_VERSION);
+        int versionHeaderEndIndex = getBundleVersionHeaderEndIndex(location, 
versionHeaderStartIndex);
+
+        // BUNDLE_VERSION.length() + 1 will include '='
+        String currentVersion = location.substring(versionHeaderStartIndex + 
BUNDLE_VERSION.length() + 1,
+                versionHeaderEndIndex + 1);
+        if (currentVersion.charAt(0) == '$' || 
!currentVersion.equals(getValidVersion(location, currentVersion))) {
+            String currentBundleVersionHeader = 
location.substring(versionHeaderStartIndex, versionHeaderEndIndex + 1);
+
+            return location.replace(currentBundleVersionHeader, 
bundleVersioHeader);
+        }
+
+        return location;
+    }
+
+    /**
+     * @return wrap protocol Bundle-Version header last char index, inclusive
+     */
+    int getBundleVersionHeaderEndIndex(String location, int 
versionHeaderStartIndex) {
+        for (int i = versionHeaderStartIndex; i < location.length(); i++) {
+            if (location.charAt(i) == '&' || location.charAt(i) == ';') {
+                return i - 1;
+            }
+        }
+
+        return location.length() - 1;
+    }
+}
\ No newline at end of file
diff --git 
a/tooling/camel-karaf-feature-maven-plugin/src/test/java/org/apache/camel/karaf/feature/maven/EnsureWrapBundleVersionMojoTest.java
 
b/tooling/camel-karaf-feature-maven-plugin/src/test/java/org/apache/camel/karaf/feature/maven/EnsureWrapBundleVersionMojoTest.java
new file mode 100644
index 000000000..974750717
--- /dev/null
+++ 
b/tooling/camel-karaf-feature-maven-plugin/src/test/java/org/apache/camel/karaf/feature/maven/EnsureWrapBundleVersionMojoTest.java
@@ -0,0 +1,143 @@
+/*
+ * 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.camel.karaf.feature.maven;
+
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+public class EnsureWrapBundleVersionMojoTest {
+
+    private final EnsureWrapBundleVersionMojo ensureVersionMojo = new 
EnsureWrapBundleVersionMojo();
+
+    @Test
+    void modifyLocationTest() throws Exception {
+        // add bundle version at the end as first wrap protocol option
+        String location = "wrap:mvn:org.apache.olingo/odata-server-core/5.0.0";
+        String expected = 
"wrap:mvn:org.apache.olingo/odata-server-core/5.0.0$Bundle-Version=5.0.0";
+        assertEquals(expected, ensureVersionMojo.processLocation(location));
+
+        // add bundle version at the end but not as first wrap protocol option
+        location = 
"wrap:mvn:org.apache.olingo/odata-server-core/5.0.0$overwrite=merge";
+        expected = 
"wrap:mvn:org.apache.olingo/odata-server-core/5.0.0$overwrite=merge&Bundle-Version=5.0.0";
+        assertEquals(expected, ensureVersionMojo.processLocation(location));
+
+        // add bundle version before existing wrap protocol header that should 
be declared after
+        location = 
"wrap:mvn:org.apache.olingo/odata-server-core/5.0.0$overwrite=merge&Export-Package=org.apache.olingo.*;version=5.0.0";
+        expected = 
"wrap:mvn:org.apache.olingo/odata-server-core/5.0.0$overwrite=merge&Bundle-Version=5.0.0&Export-Package=org.apache.olingo.*;version=5.0.0";
+        assertEquals(expected, ensureVersionMojo.processLocation(location));
+
+        // original version won't work in Karaf
+        location = 
"wrap:mvn:com.google.apis/google-api-services-storage/v1-rev20240209-2.0.0";
+        expected = 
"wrap:mvn:com.google.apis/google-api-services-storage/v1-rev20240209-2.0.0$Bundle-Version=0.0.0.v1-rev20240209-2_0_0";
+        assertEquals(expected, ensureVersionMojo.processLocation(location));
+
+        // bundle version header is present but it won't work in Karaf
+        location = 
"wrap:mvn:com.google.apis/google-api-services-storage/v1-rev20240209-2.0.0$Bundle-Version=v1-rev20240209-2.0.0";
+        expected = 
"wrap:mvn:com.google.apis/google-api-services-storage/v1-rev20240209-2.0.0$Bundle-Version=0.0.0.v1-rev20240209-2_0_0";
+        assertEquals(expected, ensureVersionMojo.processLocation(location));
+
+        // bundle version header is present and points to the wrong value but 
it will
+        // work in Karaf
+        location = "mvn:commons-io/commons-io/2.15.1$Bundle-Version=2.15.0";
+        expected = "mvn:commons-io/commons-io/2.15.1$Bundle-Version=2.15.0";
+        assertEquals(expected, ensureVersionMojo.processLocation(location));
+    }
+
+    @Test
+    void getVersionStartIndexTest() {
+        assertEquals(51,
+                
ensureVersionMojo.getVersionStartIndex("wrap:mvn:org.apache.httpcomponents.core5/httpcore5/5.2.1"));
+        assertEquals(51, ensureVersionMojo.getVersionStartIndex(
+                
"wrap:mvn:org.eclipse.californium/element-connector/3.11.0$overwrite=merge&Import-Package=net.i2p.crypto.eddsa;resolution:=optional"));
+    }
+
+    @Test
+    void getVersionEndIndexTest() {
+        assertEquals(55,
+                
ensureVersionMojo.getVersionEndIndex("wrap:mvn:org.apache.httpcomponents.core5/httpcore5/5.2.1"));
+        assertEquals(56, ensureVersionMojo.getVersionEndIndex(
+                
"wrap:mvn:org.eclipse.californium/element-connector/3.11.0$overwrite=merge&Import-Package=net.i2p.crypto.eddsa;resolution:=optional"));
+    }
+
+    @Test
+    void getVersionTest() {
+        assertEquals("${google-oauth-client-version}", 
ensureVersionMojo.getVersion(
+                
"wrap:mvn:com.google.oauth-client/google-oauth-client-jetty/${google-oauth-client-version}$overwrite=merge&Import-Package=com.sun.net.httpserver;resolution:=optional,*"));
+
+        assertEquals("8.44.0.Final", 
ensureVersionMojo.getVersion("wrap:mvn:org.kie/kie-api/8.44.0.Final"));
+
+        assertEquals("5.0.0", ensureVersionMojo.getVersion(
+                
"wrap:mvn:org.apache.olingo/odata-server-core/5.0.0$overwrite=merge&Export-Package=org.apache.olingo.*;version=5.0.0"));
+
+        assertEquals("${grpc-version}",
+                
ensureVersionMojo.getVersion("wrap:mvn:io.grpc/grpc-core/${grpc-version}$${spi-provider}"));
+
+        assertEquals("1.63.0", 
ensureVersionMojo.getVersion("wrap:mvn:io.grpc/grpc-googleapis/1.63.0$SPI-Provider=*"));
+    }
+
+    @Test
+    void getValidVersionTest() throws Exception {
+        // raw version is already valid
+        assertEquals("1.2.3", ensureVersionMojo.getValidVersion("", "1.2.3"));
+        assertEquals("1.0.0", ensureVersionMojo.getValidVersion("", "1.0"));
+
+        // raw version is invalid
+        assertEquals("0.0.0.SRU2023-10_1_4", 
ensureVersionMojo.getValidVersion("", "SRU2023-10.1.4"));
+        assertEquals("1.23.1.alpha", ensureVersionMojo.getValidVersion("", 
"1.23.1-alpha"));
+        assertEquals("0.0.0.v3-rev20240123-2_0_0", 
ensureVersionMojo.getValidVersion("", "v3-rev20240123-2.0.0"));
+        assertEquals("1.1.0.4c", ensureVersionMojo.getValidVersion("", 
"1.1.4c"));
+    }
+
+    @Test
+    void updateExistingVersionTest() throws Exception {
+        String bundleVersionHeader = "Bundle-Version=9.9.9";
+
+        // no update test
+        String location = 
"wrap:mvn:org.apache.olingo/odata-server-core/5.0.0$Bundle-Version=4.4.4";
+        String expected = 
"wrap:mvn:org.apache.olingo/odata-server-core/5.0.0$Bundle-Version=4.4.4";
+        String result = ensureVersionMojo.updateExistingVersion(location, 
bundleVersionHeader);
+        assertEquals(expected, result);
+
+        location = 
"wrap:mvn:org.apache.olingo/odata-server-core/5.0.0$Bundle-Version=${unresolved-placeholder-version}";
+        expected = 
"wrap:mvn:org.apache.olingo/odata-server-core/5.0.0$Bundle-Version=9.9.9";
+        result = ensureVersionMojo.updateExistingVersion(location, 
bundleVersionHeader);
+        assertEquals(expected, result);
+
+        location = 
"wrap:mvn:org.apache.olingo/odata-server-core/5.0.0$Bundle-Version=invalid_4.4.4";
+        expected = 
"wrap:mvn:org.apache.olingo/odata-server-core/5.0.0$Bundle-Version=9.9.9";
+        result = ensureVersionMojo.updateExistingVersion(location, 
bundleVersionHeader);
+        assertEquals(expected, result);
+
+        location = 
"wrap:mvn:org.eclipse.californium/element-connector/${californium-version}$overwrite=merge&Bundle-Version=invalid_4.4.4&Import-Package=net.i2p.crypto.eddsa;resolution:=optional";
+        expected = 
"wrap:mvn:org.eclipse.californium/element-connector/${californium-version}$overwrite=merge&Bundle-Version=9.9.9&Import-Package=net.i2p.crypto.eddsa;resolution:=optional";
+        result = ensureVersionMojo.updateExistingVersion(location, 
bundleVersionHeader);
+        assertEquals(expected, result);
+    }
+
+    @Test
+    void getBundleVersionHeaderEndIndexTest() {
+        String location = 
"wrap:mvn:org.apache.qpid/qpid-jms-client/${qpid-jms-client-version}$Bundle-Version=${qpid-jms-client-version}&Import-Package=net.i2p.crypto.eddsa;resolution:=optional";
+        int versionHeaderStartIndex = 
location.indexOf(EnsureWrapBundleVersionMojo.BUNDLE_VERSION);
+        assertEquals(108, 
ensureVersionMojo.getBundleVersionHeaderEndIndex(location, 
versionHeaderStartIndex));
+
+        location = 
"wrap:mvn:org.apache.qpid/qpid-jms-client/${qpid-jms-client-version}$Bundle-Version=${qpid-jms-client-version}";
+        versionHeaderStartIndex = 
location.indexOf(EnsureWrapBundleVersionMojo.BUNDLE_VERSION);
+        assertEquals(108, 
ensureVersionMojo.getBundleVersionHeaderEndIndex(location, 
versionHeaderStartIndex));
+    }
+}
\ No newline at end of file
diff --git a/tooling/pom.xml b/tooling/pom.xml
new file mode 100644
index 000000000..aa47d0d65
--- /dev/null
+++ b/tooling/pom.xml
@@ -0,0 +1,35 @@
+<!--
+    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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
https://maven.apache.org/xsd/maven-4.0.0.xsd";>
+    <modelVersion>4.0.0</modelVersion>
+    
+    <parent>
+        <groupId>org.apache.camel.karaf</groupId>
+        <artifactId>camel-karaf</artifactId>
+        <version>4.6.0-SNAPSHOT</version>
+    </parent>
+    
+    <artifactId>tooling</artifactId>
+    <packaging>pom</packaging>
+    <name>Apache Camel :: Karaf :: Tooling</name>
+    
+    <modules>
+        <module>camel-karaf-feature-maven-plugin</module>
+    </modules>
+    
+</project>
\ No newline at end of file

Reply via email to