SLING-11685 use feature launcher assembly to launch (#4)
unpack the feature launcher assembly and use the contained script
diff --git a/pom.xml b/pom.xml
index e9de3d1..f110a09 100644
--- a/pom.xml
+++ b/pom.xml
@@ -83,6 +83,12 @@
</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>
<version>1.1</version>
<scope>compile</scope>
diff --git a/src/it/simple-for-SLING-10956-it/pom.xml b/src/it/simple-for-SLING-10956-it/pom.xml
new file mode 100644
index 0000000..35db3e7
--- /dev/null
+++ b/src/it/simple-for-SLING-10956-it/pom.xml
@@ -0,0 +1,161 @@
+<?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>simple-for-SLING-10956-it</artifactId>
+ <version>1.0-SNAPSHOT</version>
+
+ <description>A simple IT verifying starting and stopping.</description>
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-api</artifactId>
+ <version>5.6.2</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-engine</artifactId>
+ <version>5.6.2</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpclient</artifactId>
+ <version>4.5.12</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>8</source>
+ <target>8</target>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <version>3.2.0</version>
+ <executions>
+ <execution>
+ <id>reserve-network-port</id>
+ <goals>
+ <goal>reserve-network-port</goal>
+ </goals>
+ <phase>process-resources</phase>
+ <configuration>
+ <portNames>
+ <portName>http.port</portName>
+ </portNames>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>slingfeature-maven-plugin</artifactId>
+ <version>1.3.6</version>
+ <extensions>true</extensions>
+ <!-- TODO - seems we can't attach non-aggregates -->
+ <configuration>
+ <aggregates>
+ <aggregate>
+ <classifier>main</classifier>
+ <filesInclude>model.json</filesInclude>
+ </aggregate>
+ </aggregates>
+ </configuration>
+ <executions>
+ <execution>
+ <goals>
+ <goal>attach-features</goal>
+ <goal>aggregate-features</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>@project.groupId@</groupId>
+ <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-for-SLING-10956-it</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ <classifier>main</classifier>
+ <type>slingosgifeature</type>
+ </feature>
+ <launcherArguments>
+ <vmOptions>
+ <value>-DTEST_VM_OPTION=TEST_VM_OPTION_VALUE</value>
+ </vmOptions>
+ <frameworkProperties>
+ <org.osgi.service.http.port>${http.port}</org.osgi.service.http.port>
+ </frameworkProperties>
+ <variables>
+ <TEST_VARIABLE>TEST_VALUE</TEST_VARIABLE>
+ </variables>
+ </launcherArguments>
+ <startTimeoutSeconds>180</startTimeoutSeconds>
+ </launch>
+ </launches>
+ <toLaunch>
+ </toLaunch>
+ </configuration>
+ <executions>
+ <execution>
+ <goals>
+ <goal>start</goal>
+ <goal>stop</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <artifactId>maven-failsafe-plugin</artifactId>
+ <version>2.22.2</version>
+ <configuration>
+ <systemPropertyVariables>
+ <HTTP_PORT>${http.port}</HTTP_PORT>
+ <build.log.file>${project.build.directory}/../build.log</build.log.file>
+ </systemPropertyVariables>
+ </configuration>
+ <executions>
+ <execution>
+ <goals>
+ <goal>integration-test</goal>
+ <goal>verify</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
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.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.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 @@
*/
@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 @@
@Component
private ProcessTracker processes;
-
- @Override
+
+ /**
+ * To look up UnArchiver implementations
+ */
+ @Component
+ private ArchiverManager archiverManager;
+
public void execute() throws MojoExecutionException, MojoFailureException {
- Artifact launcherArtifact = new DefaultArtifact("org.apache.sling:org.apache.sling.feature.launcher:" + featureLauncherVersion);
-
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 @@
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 @@
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 @@
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();