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

rombert pushed a commit to branch issue/SLING-12790
in repository 
https://gitbox.apache.org/repos/asf/sling-feature-launcher-maven-plugin.git

commit 21ac67591aa34aff321ce645dfc82d50aea4a68b
Author: Robert Munteanu <[email protected]>
AuthorDate: Tue May 20 12:54:13 2025 +0200

    SLING-12790 - Allow launching from a feature that is not attached to the 
project
    
    Add a 'featureFile' parameter to the launch configuration.
---
 src/it/feature-file-it/pom.xml                     | 55 ++++++++++++++++++++++
 src/it/feature-file-it/src/model.json              | 14 ++++++
 src/it/feature-file-it/verify.groovy               | 21 +++++++++
 .../sling/maven/feature/launcher/Launch.java       | 30 ++++++++++--
 .../sling/maven/feature/launcher/StartMojo.java    | 24 +++++++---
 .../sling/maven/feature/launcher/LaunchTest.java   | 44 +++++++++++++++--
 6 files changed, 173 insertions(+), 15 deletions(-)

diff --git a/src/it/feature-file-it/pom.xml b/src/it/feature-file-it/pom.xml
new file mode 100644
index 0000000..0f04f72
--- /dev/null
+++ b/src/it/feature-file-it/pom.xml
@@ -0,0 +1,55 @@
+<?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. -->
+<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 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>org.apache.sling</groupId>
+    <artifactId>feature-file-it</artifactId>
+    <version>1.0-SNAPSHOT</version>
+
+    <description>An IT verifying using a local feature file 
works.</description>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>@project.groupId@</groupId>
+                <artifactId>@project.artifactId@</artifactId>
+                <version>@project.version@</version>
+                <configuration>
+                    <launches>
+                        <launch>
+                            <id>model</id>
+                            
<featureFile>${project.basedir}/src/model.json</featureFile>
+                            <startTimeoutSeconds>180</startTimeoutSeconds>
+                        </launch>
+                    </launches>
+                    <toLaunch>
+                    </toLaunch>
+                </configuration>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>start</goal>
+                            <goal>stop</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/src/it/feature-file-it/src/model.json 
b/src/it/feature-file-it/src/model.json
new file mode 100644
index 0000000..ee05e71
--- /dev/null
+++ b/src/it/feature-file-it/src/model.json
@@ -0,0 +1,14 @@
+{
+  "id":"${project.groupId}:${project.artifactId}:model:${project.version}",
+  "bundles": [
+    "org.apache.felix/org.apache.felix.scr/2.1.20",
+    "org.apache.felix/org.apache.felix.log/1.2.2",
+    "org.apache.felix/org.apache.felix.configadmin/1.9.16",
+    "org.osgi/org.osgi.util.promise/1.1.1",
+    "org.osgi/org.osgi.util.function/1.1.0",
+    "org.osgi/org.osgi.util.converter/1.0.1",
+    "org.apache.commons/commons-lang3/3.9",
+    "org.apache.felix/org.apache.felix.http.jetty/4.0.16",
+    "org.apache.felix/org.apache.felix.http.servlet-api/1.1.2"
+  ]
+}
\ No newline at end of file
diff --git a/src/it/feature-file-it/verify.groovy 
b/src/it/feature-file-it/verify.groovy
new file mode 100644
index 0000000..e9b7465
--- /dev/null
+++ b/src/it/feature-file-it/verify.groovy
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+File touchFile = new File( basedir, "build.log" );
+
+assert touchFile.isFile()
diff --git a/src/main/java/org/apache/sling/maven/feature/launcher/Launch.java 
b/src/main/java/org/apache/sling/maven/feature/launcher/Launch.java
index bf4aaca..0e4b3ff 100644
--- a/src/main/java/org/apache/sling/maven/feature/launcher/Launch.java
+++ b/src/main/java/org/apache/sling/maven/feature/launcher/Launch.java
@@ -18,11 +18,13 @@
  */
 package org.apache.sling.maven.feature.launcher;
 
+import java.io.File;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.regex.Pattern;
 
 import org.apache.maven.model.Dependency;
@@ -33,6 +35,7 @@ public class Launch {
 
     private String id;
     private Dependency feature;
+    private String featureFile;
     private LauncherArguments launcherArguments = new LauncherArguments();
     private int startTimeoutSeconds = 30;
     private boolean skip = false;
@@ -47,13 +50,21 @@ public class Launch {
         this.id = id;
     }
 
-    public Dependency getFeature() {
-        return feature;
+    public Optional<Dependency> getFeature() {
+        return Optional.ofNullable(feature);
     }
 
     public void setFeature(Dependency feature) {
         this.feature = feature;
     }
+    
+    public Optional<File> getFeatureFile() {
+        return Optional.ofNullable(featureFile).map(File::new);
+    }
+    
+    public void setFeatureFile(String featureFile) {
+        this.featureFile = featureFile;
+    }
 
     public LauncherArguments getLauncherArguments() {
         return launcherArguments;
@@ -107,10 +118,19 @@ public class Launch {
         if ( startTimeoutSeconds < 0 )
             throwInvalid("startTimeout value '" + startTimeoutSeconds + "' is 
negative" );
         
-        if ( feature == null )
-            throwInvalid("required field 'feature' is missing");
+        boolean hasFeature = feature != null;
+        boolean hasFeatureFile = featureFile != null && 
!featureFile.trim().isEmpty();
+        
+        if ( hasFeature && hasFeatureFile )
+            throwInvalid("Only one of 'feature' and 'featureFile' is allowed, 
but both are set");
+        
+        if ( !hasFeature && !hasFeatureFile )
+            throwInvalid("Neither 'feature' nor 'featureFile' are set");
+        
+        if ( hasFeatureFile && ! new File(featureFile).exists() )
+            throwInvalid("Feature file '" + featureFile + "' does not exist");
         
-        if ( ! "slingosgifeature".equals(feature.getType()) )
+        if ( hasFeature && ! "slingosgifeature".equals(feature.getType()) )
             throwInvalid("type must be 'slingosgifeature' but is '" + 
feature.getType()+"'");
     }
     
diff --git 
a/src/main/java/org/apache/sling/maven/feature/launcher/StartMojo.java 
b/src/main/java/org/apache/sling/maven/feature/launcher/StartMojo.java
index dc18b84..e7a1734 100644
--- a/src/main/java/org/apache/sling/maven/feature/launcher/StartMojo.java
+++ b/src/main/java/org/apache/sling/maven/feature/launcher/StartMojo.java
@@ -85,8 +85,9 @@ public class StartMojo extends AbstractMojo {
     /**
      * List of {@link Launch} objects to start. Each is having the following 
format:
      * <pre>{@code
-     * <id>...</id> <!-- the id of the launch, must be unique within the list, 
is mandatory -->
-     * <dependency>...</dependency> <!-- the Maven coordinates of the feature 
model -->
+     * <id>...</id> <!-- the id of the launch, must be unique within the list, 
is mandatory-->
+     * <feature>...</feature> <!-- the Maven coordinates of the feature model, 
mandatory unless featureFile is used  -->
+     * <featureFile>...</featureFile> <!-- the path to the feature model, 
mandatory unless feature is used -->
      * <launcherArguments> <!-- additional arguments to pass to the launcher 
-->
      *   <frameworkProperties>
      *     <org.osgi.service.http.port>8090</org.osgi.service.http.port>
@@ -185,12 +186,13 @@ public class StartMojo extends AbstractMojo {
                 }
 
                 launch.validate();
-
-                Artifact artifact = toArtifact(launch.getFeature());
                 
-                ArtifactResult result = 
resolver.resolveArtifact(repositorySession, new ArtifactRequest(artifact, 
remoteRepos, null));
-                File featureFile = result.getArtifact().getFile();
-
+                File featureFile = launch.getFeature().
+                    map( this::toArtifact )
+                   .map( a -> uncheckedResolveArtifact(repositorySession, a) )
+                   .map( r -> r.getArtifact().getFile())
+                   .orElseGet( () -> launch.getFeatureFile().get() ); // the 
Launch is guaranteed to either have a feature or a featureFile set
+                
                 String javahome = System.getenv(JAVA_HOME);
                 if (javahome == null || javahome.isEmpty()) {
                     // SLING-9843 fallback to java.home system property if 
JAVA_HOME env variable is not set
@@ -318,6 +320,14 @@ public class StartMojo extends AbstractMojo {
         }
     }
 
+    private ArtifactResult uncheckedResolveArtifact(RepositorySystemSession 
repositorySession, Artifact artifact) {
+        try {
+            return resolver.resolveArtifact(repositorySession, new 
ArtifactRequest(artifact, remoteRepos, null));
+        } catch (ArtifactResolutionException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
     private Artifact toArtifact(Dependency dependency) {
         return new DefaultArtifact(dependency.getGroupId(), 
dependency.getArtifactId(), dependency.getClassifier(), dependency.getType(), 
dependency.getVersion());
     }
diff --git 
a/src/test/java/org/apache/sling/maven/feature/launcher/LaunchTest.java 
b/src/test/java/org/apache/sling/maven/feature/launcher/LaunchTest.java
index e512d13..1218f5c 100644
--- a/src/test/java/org/apache/sling/maven/feature/launcher/LaunchTest.java
+++ b/src/test/java/org/apache/sling/maven/feature/launcher/LaunchTest.java
@@ -18,26 +18,37 @@
  */
 package org.apache.sling.maven.feature.launcher;
 
+import java.io.File;
+import java.io.IOException;
+
 import org.apache.maven.model.Dependency;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
 
 public class LaunchTest {
+
+    @Rule
+    public TemporaryFolder tmp = new TemporaryFolder();
     
     private Dependency validDep;
-    
+    private File validFeatureFile;
+
     @Before
-    public void prepare() {
+    public void prepare() throws IOException {
         validDep  = new Dependency();
         validDep.setGroupId("org.apache.sling");
         validDep.setArtifactId("org.apache.sling.starter");
         validDep.setVersion("12");
         validDep.setClassifier("oak_tar");
         validDep.setType("slingosgifeature");
+        
+        validFeatureFile = tmp.newFile("feature.json");
     }
 
     @Test
-    public void validLaunch() {
+    public void validLaunch_withFeature() {
         
         Launch launch = new Launch();
         launch.setFeature(validDep);
@@ -45,6 +56,15 @@ public class LaunchTest {
         launch.validate();
     }
 
+    @Test
+    public void validLaunch_withFeatureFile() {
+        
+        Launch launch = new Launch();
+        launch.setFeatureFile(validFeatureFile.getAbsolutePath());
+        launch.setId("feature");
+        launch.validate();
+    }
+
     @Test(expected = IllegalArgumentException.class)
     public void invalidLaunch_noId() {
         
@@ -91,5 +111,23 @@ public class LaunchTest {
         launch.setStartTimeoutSeconds(-10);
         launch.validate();
     }
+    
+    @Test(expected = IllegalArgumentException.class)
+    public void invalidLaunch_bothFeatureAndFeatureFile() {
+
+        Launch launch = new Launch();
+        launch.setId("feature");
+        launch.setFeature(validDep);
+        launch.setFeatureFile(validFeatureFile.getAbsolutePath());
+        launch.validate();
+    }
 
+    @Test(expected = IllegalArgumentException.class)
+    public void invalidLaunch_missingFeatureFile() {
+
+        Launch launch = new Launch();
+        launch.setId("feature");
+        launch.setFeatureFile(validFeatureFile.getAbsolutePath() + ".missing");
+        launch.validate();
+    }
 }

Reply via email to