You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by en...@apache.org on 2022/11/19 03:27:48 UTC

[sling-feature-launcher-maven-plugin] branch master updated: SLING-11685 use feature launcher assembly to launch (#4)

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

enorman pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-feature-launcher-maven-plugin.git


The following commit(s) were added to refs/heads/master by this push:
     new 16063e8  SLING-11685 use feature launcher assembly to launch (#4)
16063e8 is described below

commit 16063e803ffebd915f6f5e2202581376574d9dd8
Author: Eric Norman <en...@apache.org>
AuthorDate: Fri Nov 18 19:27:44 2022 -0800

    SLING-11685 use feature launcher assembly to launch (#4)
    
    unpack the feature launcher assembly and use the contained script
---
 pom.xml                                            |   6 +
 .../pom.xml                                        |   7 +-
 .../src/main/features/model.json                   |  14 +++
 .../apache/sling/it/SLING10956/AppRunningIT.java   |  84 +++++++++++++
 src/it/simple-for-SLING-10956-it/verify.groovy     |  21 ++++
 src/it/simple-it/pom.xml                           |   3 +
 .../sling/maven/feature/launcher/StartMojo.java    | 131 +++++++++++++++++----
 7 files changed, 240 insertions(+), 26 deletions(-)

diff --git a/pom.xml b/pom.xml
index e9de3d1..f110a09 100644
--- a/pom.xml
+++ b/pom.xml
@@ -81,6 +81,12 @@
             <artifactId>javax.inject</artifactId>
             <scope>compile</scope>
         </dependency>
+        <dependency>
+            <groupId>org.codehaus.plexus</groupId>
+            <artifactId>plexus-archiver</artifactId>
+            <version>4.2.7</version>
+            <scope>compile</scope>
+        </dependency>
         <dependency>
             <groupId>org.codehaus.plexus</groupId>
             <artifactId>plexus-interactivity-api</artifactId>
diff --git a/src/it/simple-it/pom.xml b/src/it/simple-for-SLING-10956-it/pom.xml
similarity index 96%
copy from src/it/simple-it/pom.xml
copy to src/it/simple-for-SLING-10956-it/pom.xml
index 04286ed..35db3e7 100644
--- a/src/it/simple-it/pom.xml
+++ b/src/it/simple-for-SLING-10956-it/pom.xml
@@ -15,7 +15,7 @@
     <modelVersion>4.0.0</modelVersion>
 
     <groupId>org.apache.sling</groupId>
-    <artifactId>simple-it</artifactId>
+    <artifactId>simple-for-SLING-10956-it</artifactId>
     <version>1.0-SNAPSHOT</version>
 
     <description>A simple IT verifying starting and stopping.</description>
@@ -40,7 +40,7 @@
         <dependency>
             <groupId>org.apache.httpcomponents</groupId>
             <artifactId>httpclient</artifactId>
-            <version>4.5.13</version>
+            <version>4.5.12</version>
             <scope>test</scope>
         </dependency>
     </dependencies>
@@ -101,12 +101,13 @@
                 <artifactId>@project.artifactId@</artifactId>
                 <version>@project.version@</version>
                 <configuration>
+                    <featureLauncherVersion>1.2.0</featureLauncherVersion>
                     <launches>
                         <launch>
                             <id>model</id>
                             <feature>
                                 <groupId>org.apache.sling</groupId>
-                                <artifactId>simple-it</artifactId>
+                                <artifactId>simple-for-SLING-10956-it</artifactId>
                                 <version>1.0-SNAPSHOT</version>
                                 <classifier>main</classifier>
                                 <type>slingosgifeature</type>
diff --git a/src/it/simple-for-SLING-10956-it/src/main/features/model.json b/src/it/simple-for-SLING-10956-it/src/main/features/model.json
new file mode 100644
index 0000000..ee05e71
--- /dev/null
+++ b/src/it/simple-for-SLING-10956-it/src/main/features/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/simple-for-SLING-10956-it/src/test/java/org/apache/sling/it/SLING10956/AppRunningIT.java b/src/it/simple-for-SLING-10956-it/src/test/java/org/apache/sling/it/SLING10956/AppRunningIT.java
new file mode 100644
index 0000000..d7671bd
--- /dev/null
+++ b/src/it/simple-for-SLING-10956-it/src/test/java/org/apache/sling/it/SLING10956/AppRunningIT.java
@@ -0,0 +1,84 @@
+/*
+ * 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.sling.it.SLING10956;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Optional;
+import java.util.regex.Pattern;
+import java.util.stream.Stream;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.MethodOrderer.Alphanumeric;
+import org.junit.jupiter.api.TestMethodOrder;
+ import  org.apache.http.impl.client.*;
+import  org.apache.http.client.methods.*;
+
+@TestMethodOrder(Alphanumeric.class)
+public class AppRunningIT {
+
+    @Test
+    public void aaSlingAppIsUp() throws Exception {
+
+        int port = Integer.getInteger("HTTP_PORT", 8080);
+
+        try ( CloseableHttpClient httpclient = HttpClients.createDefault() ) {
+            HttpGet get = new HttpGet("http://localhost:" + port + "/");
+            for ( int i = 0; i < 30; i++ ) {
+                try ( CloseableHttpResponse response = httpclient.execute(get) ) {
+                    System.out.println("Status line = " + response.getStatusLine().toString());
+                    int statusCode = response.getStatusLine().getStatusCode();
+                    if ( (statusCode / 100 < 5 ) ) {
+                        System.out.println("App is ready");
+                        return;
+                    }
+                    Thread.sleep(1000l);
+                }
+            }
+
+            fail("App is not yet ready, failing");
+        }
+    }
+
+    @Test
+    public void bbCheckLauncherEnvironmentVarInLogs() throws Exception {
+        final String logFilename = System.getProperty("build.log.file");
+
+        // This verifies the launcherArguments vmOptions and variables from our test pom
+        final Pattern expected = Pattern.compile(".*\\-DTEST_VM_OPTION=TEST_VM_OPTION_VALUE.*");
+
+        try (Stream<String> lines = Files.lines(Paths.get(logFilename))) {
+            final Optional<String> expectedLine = lines.filter(line -> expected.matcher(line).matches()).findFirst();
+            assertTrue(expectedLine.isPresent(), "Expected pattern " + expected + " to be found in log file " + logFilename);
+        }
+    }
+
+    @Test
+    public void bbCheckLauncherCommandLineInLogs() throws Exception {
+        final String logFilename = System.getProperty("build.log.file");
+
+        // This verifies the launcherArguments vmOptions and variables from our test pom
+        final Pattern expected = Pattern.compile(".*\\-V, TEST_VARIABLE=TEST_VALUE.*");
+
+        try (Stream<String> lines = Files.lines(Paths.get(logFilename))) {
+            final Optional<String> expectedLine = lines.filter(line -> expected.matcher(line).matches()).findFirst();
+            assertTrue(expectedLine.isPresent(), "Expected pattern " + expected + " to be found in log file " + logFilename);
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/it/simple-for-SLING-10956-it/verify.groovy b/src/it/simple-for-SLING-10956-it/verify.groovy
new file mode 100644
index 0000000..e9b7465
--- /dev/null
+++ b/src/it/simple-for-SLING-10956-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/it/simple-it/pom.xml b/src/it/simple-it/pom.xml
index 04286ed..903dd04 100644
--- a/src/it/simple-it/pom.xml
+++ b/src/it/simple-it/pom.xml
@@ -101,6 +101,9 @@
                 <artifactId>@project.artifactId@</artifactId>
                 <version>@project.version@</version>
                 <configuration>
+                    <!-- pin this at the 1.1.26 version to test launching 
+                         using the pre SLING-10956 technique -->
+                    <featureLauncherVersion>1.1.26</featureLauncherVersion>
                     <launches>
                         <launch>
                             <id>model</id>
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 c1f9970..2278d6f 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
@@ -24,12 +24,18 @@ import java.io.File;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.lang.ProcessBuilder.Redirect;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
+import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
+import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
+import org.apache.maven.artifact.versioning.VersionRange;
 import org.apache.maven.execution.MavenSession;
 import org.apache.maven.model.Dependency;
 import org.apache.maven.plugin.AbstractMojo;
@@ -40,6 +46,10 @@ import org.apache.maven.plugins.annotations.LifecyclePhase;
 import org.apache.maven.plugins.annotations.Mojo;
 import org.apache.maven.plugins.annotations.Parameter;
 import org.apache.maven.project.MavenProject;
+import org.apache.maven.shared.utils.Os;
+import org.codehaus.plexus.archiver.UnArchiver;
+import org.codehaus.plexus.archiver.manager.ArchiverManager;
+import org.codehaus.plexus.archiver.manager.NoSuchArchiverException;
 import org.eclipse.aether.RepositorySystemSession;
 import org.eclipse.aether.artifact.Artifact;
 import org.eclipse.aether.artifact.DefaultArtifact;
@@ -54,7 +64,10 @@ import org.eclipse.aether.resolution.ArtifactResult;
  */
 @Mojo( name = "start", defaultPhase = LifecyclePhase.PRE_INTEGRATION_TEST )
 public class StartMojo extends AbstractMojo {
-    
+
+    private static final String JAVA_HOME = "JAVA_HOME";
+    private static final String JAVA_OPTS = "JAVA_OPTS";
+
     /**
      * The directory in which the features are launched (below its child directory {@code launchers/<launch-id>}).
      */
@@ -101,21 +114,55 @@ public class StartMojo extends AbstractMojo {
     
     @Component
     private ProcessTracker processes;
-    
-    @Override
-    public void execute() throws MojoExecutionException, MojoFailureException {
 
-        Artifact launcherArtifact = new DefaultArtifact("org.apache.sling:org.apache.sling.feature.launcher:" + featureLauncherVersion);
+    /**
+     * To look up UnArchiver implementations
+     */
+    @Component
+    private ArchiverManager archiverManager;
+
+    public void execute() throws MojoExecutionException, MojoFailureException {
 
         try {
+            // the feature launcher before version 1.1.28 used a single jar, while versions
+            //  after that provide an assembly per SLING-10956
+            VersionRange beforeAssemblyRange = VersionRange.createFromVersionSpec("(,1.1.26]");
+            boolean useAssembly = !beforeAssemblyRange.containsVersion(new DefaultArtifactVersion(featureLauncherVersion));
+
             RepositorySystemSession repositorySession = mavenSession.getRepositorySession();
-            File launcher = resolver
-                .resolveArtifact(repositorySession, new ArtifactRequest(launcherArtifact, remoteRepos, null))
-                .getArtifact()
-                .getFile();
-            
             File workDir = new File(outputDirectory, "launchers");
             workDir.mkdirs();
+
+            File launcher;
+            if (useAssembly) {
+                // fetch the assembly artifact
+                Artifact launcherAssemblyArtifact = new DefaultArtifact("org.apache.sling:org.apache.sling.feature.launcher:tar.gz:" + featureLauncherVersion);
+                File assemblyArchive = resolver
+                        .resolveArtifact(repositorySession, new ArtifactRequest(launcherAssemblyArtifact, remoteRepos, null))
+                        .getArtifact()
+                        .getFile();
+
+                // unpack the file
+                UnArchiver unArchiver = archiverManager.getUnArchiver( assemblyArchive );
+                unArchiver.setSourceFile(assemblyArchive);
+                unArchiver.setDestFile(workDir);
+                unArchiver.extract();
+
+                // system property
+                Path relPath = Paths.get(launcherAssemblyArtifact.getArtifactId() + "-" + launcherAssemblyArtifact.getVersion(), "bin");
+                if (Os.isFamily(Os.FAMILY_WINDOWS)) {
+                    relPath = relPath.resolve("launcher.bat");
+                } else {
+                    relPath = relPath.resolve("launcher");
+                }
+                launcher = workDir.toPath().resolve(relPath).toFile();
+            } else {
+                Artifact launcherArtifact = new DefaultArtifact("org.apache.sling:org.apache.sling.feature.launcher:" + featureLauncherVersion);
+                launcher = resolver
+                        .resolveArtifact(repositorySession, new ArtifactRequest(launcherArtifact, remoteRepos, null))
+                        .getArtifact()
+                        .getFile();
+            }
             
             for ( Launch launch : launches ) {
                 if (launch.isSkip()) {
@@ -129,24 +176,59 @@ public class StartMojo extends AbstractMojo {
                 
                 ArtifactResult result = resolver.resolveArtifact(repositorySession, new ArtifactRequest(artifact, remoteRepos, null));
                 File featureFile = result.getArtifact().getFile();
-                
-                List<String> args = new ArrayList<>();
-                String javahome = System.getenv("JAVA_HOME");
+
+                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
                     getLog().warn("The JAVA_HOME env variable was not set, falling back to the java.home system property");
                     javahome = System.getProperty("java.home");
                 }
-                args.add(javahome + File.separatorChar + "bin" + File.separatorChar + "java");
-                // SLING-9994 - if any extra vm options were supplied, apply them here
-                String[] vmOptions = launch.getLauncherArguments().getVmOptions();
-                for (String vmOption : vmOptions) {
-                    if (vmOption != null && !vmOption.isEmpty()) {
-                        args.add(vmOption);
+                List<String> args = new ArrayList<>();
+                if (useAssembly) {
+                    // use the post v1.1.28 launcher script
+
+                    Map<String, String> newEnv = new HashMap<>(launch.getEnvironmentVariables());
+                    newEnv.put(JAVA_HOME, javahome);
+
+                    // SLING-9994 - if any extra vm options were supplied, apply them here
+                    StringBuilder javaOptsBuilder = null;
+                    String[] vmOptions = launch.getLauncherArguments().getVmOptions();
+                    for (String vmOption : vmOptions) {
+                        if (vmOption != null && !vmOption.isEmpty()) {
+                            if (javaOptsBuilder == null) {
+                                javaOptsBuilder = new StringBuilder();
+                            } else {
+                                javaOptsBuilder.append(" ");
+                            }
+                            javaOptsBuilder.append(vmOption);
+                        }
+                    }
+                    if (javaOptsBuilder != null) {
+                        // pass vmOptions through JAVA_OPTS environment variable?
+                        if (newEnv.containsKey(JAVA_OPTS)) {
+                            // if the original value existed append it to our buffer
+                            javaOptsBuilder.append(" ").append(newEnv.get(JAVA_OPTS));
+                        }
+                        newEnv.put(JAVA_OPTS, javaOptsBuilder.toString());
+                    }
+
+                    args.add(launcher.getAbsolutePath());
+
+                    launch.setEnvironmentVariables(newEnv);
+                } else {
+                    // use the pre v1.1.28 single jar technique
+
+                    args.add(javahome + File.separatorChar + "bin" + File.separatorChar + "java");
+                    // SLING-9994 - if any extra vm options were supplied, apply them here
+                    String[] vmOptions = launch.getLauncherArguments().getVmOptions();
+                    for (String vmOption : vmOptions) {
+                        if (vmOption != null && !vmOption.isEmpty()) {
+                            args.add(vmOption);
+                        }
                     }
+                    args.add("-jar");
+                    args.add(launcher.getAbsolutePath());
                 }
-                args.add("-jar");
-                args.add(launcher.getAbsolutePath());
                 args.add("-f");
                 args.add(featureFile.getAbsolutePath());
                 args.add("-p");
@@ -168,7 +250,10 @@ public class StartMojo extends AbstractMojo {
                 pb.redirectInput(Redirect.INHERIT);
                 pb.directory(workDir);
                 launch.getEnvironmentVariables().entrySet()
-                    .forEach( e -> pb.environment().put(e.getKey(), e.getValue()) );
+                    .forEach( e -> {
+                            getLog().info("Setting environment variable '" + e.getKey() + "' to '" + e.getValue() + "'");
+                            pb.environment().put(e.getKey(), e.getValue());
+                        } );
                 
                 getLog().info("Starting launch with id '" + launch.getId() + "', args=" + args);
                 
@@ -205,7 +290,7 @@ public class StartMojo extends AbstractMojo {
                 processes.startTracking(launch.getId(), process);
             }
 
-        } catch (ArtifactResolutionException | IOException e) {
+        } catch (NoSuchArchiverException | InvalidVersionSpecificationException | ArtifactResolutionException | IOException e) {
             throw new MojoExecutionException(e.getMessage(), e);
         } catch ( InterruptedException e ) {
             Thread.currentThread().interrupt();