From 1b285aeef7c98d36820bb90e2a1ca400ddf75288 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?JB=20Onofr=C3=A9?= Date: Fri, 13 Mar 2026 17:37:31 +0100 Subject: [PATCH] fix(tooling): support non-standard feature classifier names in karaf-maven-plugin (#2144) The feature descriptor generator hardcoded the "features" classifier, causing dependencies with non-standard classifiers (e.g. "features-core") to be inlined as bundles instead of recognized as feature dependencies. - Dependency31Helper.isFeature(): match classifiers starting with "features" - GenerateDescriptorMojo.processFeatureArtifact(): remove redundant classifier check - GenerateDescriptorMojo: use configured attachmentArtifactClassifier instead of hardcoded FEATURE_CLASSIFIER when setting the project artifact - AssemblyMojo.getType() and RunMojo.getAttachedFeatureFile(): same classifier fix --- .../consumer-feature/pom.xml | 64 +++++++++++++++++ .../it/test-feature-classifier/control.xml | 26 +++++++ .../custom-classifier-feature/pom.xml | 62 ++++++++++++++++ .../invoker.properties | 20 ++++++ .../src/it/test-feature-classifier/pom.xml | 61 ++++++++++++++++ .../test-bundle/pom.xml | 50 +++++++++++++ .../src/main/java/test/TestClass.java | 25 +++++++ .../src/it/test-feature-classifier/verify.bsh | 33 +++++++++ .../apache/karaf/tooling/AssemblyMojo.java | 2 +- .../org/apache/karaf/tooling/RunMojo.java | 2 +- .../features/GenerateDescriptorMojo.java | 7 +- .../tooling/utils/Dependency31Helper.java | 3 +- .../tooling/utils/Dependency31HelperTest.java | 71 +++++++++++++++++++ 13 files changed, 418 insertions(+), 8 deletions(-) create mode 100644 tooling/karaf-maven-plugin/src/it/test-feature-classifier/consumer-feature/pom.xml create mode 100644 tooling/karaf-maven-plugin/src/it/test-feature-classifier/control.xml create mode 100644 tooling/karaf-maven-plugin/src/it/test-feature-classifier/custom-classifier-feature/pom.xml create mode 100644 tooling/karaf-maven-plugin/src/it/test-feature-classifier/invoker.properties create mode 100644 tooling/karaf-maven-plugin/src/it/test-feature-classifier/pom.xml create mode 100644 tooling/karaf-maven-plugin/src/it/test-feature-classifier/test-bundle/pom.xml create mode 100644 tooling/karaf-maven-plugin/src/it/test-feature-classifier/test-bundle/src/main/java/test/TestClass.java create mode 100644 tooling/karaf-maven-plugin/src/it/test-feature-classifier/verify.bsh create mode 100644 tooling/karaf-maven-plugin/src/test/java/org/apache/karaf/tooling/utils/Dependency31HelperTest.java diff --git a/tooling/karaf-maven-plugin/src/it/test-feature-classifier/consumer-feature/pom.xml b/tooling/karaf-maven-plugin/src/it/test-feature-classifier/consumer-feature/pom.xml new file mode 100644 index 00000000000..6227823b6fa --- /dev/null +++ b/tooling/karaf-maven-plugin/src/it/test-feature-classifier/consumer-feature/pom.xml @@ -0,0 +1,64 @@ + + + + + 4.0.0 + + test + test-feature-classifier + 1.0-SNAPSHOT + ../pom.xml + + + test + consumer-feature + 1.0-SNAPSHOT + feature + + + UTF-8 + + + + + + test + custom-classifier-feature + 1.0-SNAPSHOT + features-core + xml + + + + + + + org.apache.karaf.tooling + karaf-maven-plugin + @pom.version@ + true + + true + + + + + diff --git a/tooling/karaf-maven-plugin/src/it/test-feature-classifier/control.xml b/tooling/karaf-maven-plugin/src/it/test-feature-classifier/control.xml new file mode 100644 index 00000000000..6823b8a01b4 --- /dev/null +++ b/tooling/karaf-maven-plugin/src/it/test-feature-classifier/control.xml @@ -0,0 +1,26 @@ + + + + + mvn:test/custom-classifier-feature/1.0-SNAPSHOT/xml/features-core + + custom-classifier-feature + + diff --git a/tooling/karaf-maven-plugin/src/it/test-feature-classifier/custom-classifier-feature/pom.xml b/tooling/karaf-maven-plugin/src/it/test-feature-classifier/custom-classifier-feature/pom.xml new file mode 100644 index 00000000000..6f3681bb238 --- /dev/null +++ b/tooling/karaf-maven-plugin/src/it/test-feature-classifier/custom-classifier-feature/pom.xml @@ -0,0 +1,62 @@ + + + + + 4.0.0 + + test + test-feature-classifier + 1.0-SNAPSHOT + ../pom.xml + + + test + custom-classifier-feature + 1.0-SNAPSHOT + feature + + + UTF-8 + + + + + test + test-bundle + ${project.version} + + + + + + + org.apache.karaf.tooling + karaf-maven-plugin + @pom.version@ + true + + true + features-core + + + + + diff --git a/tooling/karaf-maven-plugin/src/it/test-feature-classifier/invoker.properties b/tooling/karaf-maven-plugin/src/it/test-feature-classifier/invoker.properties new file mode 100644 index 00000000000..7c40a751357 --- /dev/null +++ b/tooling/karaf-maven-plugin/src/it/test-feature-classifier/invoker.properties @@ -0,0 +1,20 @@ +# +# 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. +# + +invoker.goals = install diff --git a/tooling/karaf-maven-plugin/src/it/test-feature-classifier/pom.xml b/tooling/karaf-maven-plugin/src/it/test-feature-classifier/pom.xml new file mode 100644 index 00000000000..ae10ec5babd --- /dev/null +++ b/tooling/karaf-maven-plugin/src/it/test-feature-classifier/pom.xml @@ -0,0 +1,61 @@ + + + + + + 4.0.0 + + test + test-feature-classifier + 1.0-SNAPSHOT + pom + + + UTF-8 + + + + + + test-bundle + custom-classifier-feature + consumer-feature + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + 256M + ${compiler.fork} + + + + org.apache.felix + maven-bundle-plugin + true + + + + + diff --git a/tooling/karaf-maven-plugin/src/it/test-feature-classifier/test-bundle/pom.xml b/tooling/karaf-maven-plugin/src/it/test-feature-classifier/test-bundle/pom.xml new file mode 100644 index 00000000000..aa0b5eb2915 --- /dev/null +++ b/tooling/karaf-maven-plugin/src/it/test-feature-classifier/test-bundle/pom.xml @@ -0,0 +1,50 @@ + + + + + 4.0.0 + + test + test-feature-classifier + 1.0-SNAPSHOT + ../pom.xml + + + test + test-bundle + 1.0-SNAPSHOT + bundle + + + UTF-8 + + + + + + org.apache.felix + maven-bundle-plugin + 2.3.7 + true + + + + diff --git a/tooling/karaf-maven-plugin/src/it/test-feature-classifier/test-bundle/src/main/java/test/TestClass.java b/tooling/karaf-maven-plugin/src/it/test-feature-classifier/test-bundle/src/main/java/test/TestClass.java new file mode 100644 index 00000000000..75368fdcced --- /dev/null +++ b/tooling/karaf-maven-plugin/src/it/test-feature-classifier/test-bundle/src/main/java/test/TestClass.java @@ -0,0 +1,25 @@ +/* + * 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 test; + +public class TestClass +{ + public static String VALUE = "test"; +} diff --git a/tooling/karaf-maven-plugin/src/it/test-feature-classifier/verify.bsh b/tooling/karaf-maven-plugin/src/it/test-feature-classifier/verify.bsh new file mode 100644 index 00000000000..edf7e9274a7 --- /dev/null +++ b/tooling/karaf-maven-plugin/src/it/test-feature-classifier/verify.bsh @@ -0,0 +1,33 @@ +/* + * 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. + */ + +import org.custommonkey.xmlunit.*; +import java.io.*; +import java.lang.*; + +Reader r = new FileReader(new File(basedir, "control.xml")); + +// load the features file pushed to the repository +File generated = new File(basedir, "consumer-feature/target/feature/feature.xml" ); +if (generated.exists()) { + XMLAssert.assertXMLEqual(r, new FileReader(generated)); + return true; +} + +return false; diff --git a/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/AssemblyMojo.java b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/AssemblyMojo.java index f1824a35333..1dd1325a053 100644 --- a/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/AssemblyMojo.java +++ b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/AssemblyMojo.java @@ -838,7 +838,7 @@ private String getType(Artifact artifact) { } } // Identify features - if ("features".equals(artifact.getClassifier())) { + if (artifact.getClassifier() != null && artifact.getClassifier().startsWith("features")) { return "features"; } if ("xml".equals(artifact.getType())) { diff --git a/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/RunMojo.java b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/RunMojo.java index 570e6fffc52..c980104dbe5 100644 --- a/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/RunMojo.java +++ b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/RunMojo.java @@ -545,7 +545,7 @@ private static boolean present(String part) { private File getAttachedFeatureFile(MavenProject project) { List attachedArtifacts = project.getAttachedArtifacts(); for (Artifact artifact : attachedArtifacts) { - if ("features".equals(artifact.getClassifier()) && "xml".equals(artifact.getType())) { + if (artifact.getClassifier() != null && artifact.getClassifier().startsWith("features") && "xml".equals(artifact.getType())) { return artifact.getFile(); } } diff --git a/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/features/GenerateDescriptorMojo.java b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/features/GenerateDescriptorMojo.java index 63575529691..749844cb5ca 100644 --- a/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/features/GenerateDescriptorMojo.java +++ b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/features/GenerateDescriptorMojo.java @@ -18,8 +18,6 @@ package org.apache.karaf.tooling.features; import static java.lang.String.format; -import static org.apache.karaf.deployer.kar.KarArtifactInstaller.FEATURE_CLASSIFIER; - import java.io.*; import java.nio.file.Files; import java.util.ArrayList; @@ -370,7 +368,7 @@ public void execute() throws MojoExecutionException, MojoFailureException { if (project.getPackaging().equals("feature") && enableGeneration) { getLog().info("Set artifact"); Artifact artifact = factory.createArtifactWithClassifier(project.getGroupId(), project.getArtifactId(), project.getVersion(), attachmentArtifactType - , FEATURE_CLASSIFIER); + , attachmentArtifactClassifier); artifact.setFile(outputFile); project.setArtifact(artifact); } else { @@ -611,8 +609,7 @@ private void processFeatureArtifact(Features features, Feature feature, Map featureRepositories, FeaturesCache cache, Object artifact, Object parent, boolean add) throws MojoExecutionException, XMLStreamException, JAXBException, IOException { - if (this.dependencyHelper.isArtifactAFeature(artifact) && FEATURE_CLASSIFIER.equals( - this.dependencyHelper.getClassifier(artifact))) { + if (this.dependencyHelper.isArtifactAFeature(artifact)) { File featuresFile = this.dependencyHelper.resolve(artifact, getLog()); if (featuresFile == null || !featuresFile.exists()) { throw new MojoExecutionException( diff --git a/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/utils/Dependency31Helper.java b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/utils/Dependency31Helper.java index 44bd3d0fa2e..2c243489c82 100644 --- a/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/utils/Dependency31Helper.java +++ b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/utils/Dependency31Helper.java @@ -284,7 +284,8 @@ public static boolean isFeature(DependencyNode dependencyNode) { } public static boolean isFeature(Artifact artifact) { - return artifact.getExtension().equals("kar") || FEATURE_CLASSIFIER.equals(artifact.getClassifier()); + return artifact.getExtension().equals("kar") + || (artifact.getClassifier() != null && artifact.getClassifier().startsWith(FEATURE_CLASSIFIER)); } @Override diff --git a/tooling/karaf-maven-plugin/src/test/java/org/apache/karaf/tooling/utils/Dependency31HelperTest.java b/tooling/karaf-maven-plugin/src/test/java/org/apache/karaf/tooling/utils/Dependency31HelperTest.java new file mode 100644 index 00000000000..66ba03e5b15 --- /dev/null +++ b/tooling/karaf-maven-plugin/src/test/java/org/apache/karaf/tooling/utils/Dependency31HelperTest.java @@ -0,0 +1,71 @@ +/* + * 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.karaf.tooling.utils; + +import org.eclipse.aether.artifact.DefaultArtifact; +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class Dependency31HelperTest { + + @Test + public void testIsFeatureWithStandardClassifier() { + // groupId:artifactId:extension:classifier:version + DefaultArtifact artifact = new DefaultArtifact("org.foo:bar:xml:features:1.0"); + assertTrue(Dependency31Helper.isFeature(artifact)); + } + + @Test + public void testIsFeatureWithNonStandardClassifier() { + DefaultArtifact artifact = new DefaultArtifact("org.foo:bar:xml:features-core:1.0"); + assertTrue(Dependency31Helper.isFeature(artifact)); + } + + @Test + public void testIsFeatureWithAnotherNonStandardClassifier() { + DefaultArtifact artifact = new DefaultArtifact("org.foo:bar:xml:features-extra:1.0"); + assertTrue(Dependency31Helper.isFeature(artifact)); + } + + @Test + public void testIsFeatureWithKarExtension() { + DefaultArtifact artifact = new DefaultArtifact("org.foo:bar:kar:1.0"); + assertTrue(Dependency31Helper.isFeature(artifact)); + } + + @Test + public void testIsFeatureWithJarExtensionNoClassifier() { + DefaultArtifact artifact = new DefaultArtifact("org.foo:bar:jar:1.0"); + assertFalse(Dependency31Helper.isFeature(artifact)); + } + + @Test + public void testIsFeatureWithUnrelatedClassifier() { + DefaultArtifact artifact = new DefaultArtifact("org.foo:bar:xml:sources:1.0"); + assertFalse(Dependency31Helper.isFeature(artifact)); + } + + @Test + public void testIsFeatureWithEmptyClassifier() { + DefaultArtifact artifact = new DefaultArtifact("org.foo:bar:xml:1.0"); + assertFalse(Dependency31Helper.isFeature(artifact)); + } +}