Extract AotGenerateMojo to its own structure
This commit stops AotGenerateMojo from being an extension of the regular run infrastructure and used the opportunity to extract a number of utility classes to run a Java process. As a result, not all features of running an application is supported and exposed options now are targeted against AOT. See gh-31682pull/31928/head
parent
197b4eede0
commit
6f8b9288f3
@ -0,0 +1,135 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2022 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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
|
||||||
|
*
|
||||||
|
* https://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.springframework.boot.maven;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class to build the command-line arguments of a java process.
|
||||||
|
*
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
*/
|
||||||
|
final class CommandLineBuilder {
|
||||||
|
|
||||||
|
private final List<String> options = new ArrayList<>();
|
||||||
|
|
||||||
|
private final List<URL> classpathElements = new ArrayList<>();
|
||||||
|
|
||||||
|
private final String mainClass;
|
||||||
|
|
||||||
|
private final List<String> arguments = new ArrayList<>();
|
||||||
|
|
||||||
|
private CommandLineBuilder(String mainClass) {
|
||||||
|
this.mainClass = mainClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
static CommandLineBuilder forMainClass(String mainClass) {
|
||||||
|
return new CommandLineBuilder(mainClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandLineBuilder withJvmArguments(String... jvmArguments) {
|
||||||
|
if (jvmArguments != null) {
|
||||||
|
this.options.addAll(Arrays.stream(jvmArguments).filter(Objects::nonNull).toList());
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandLineBuilder withSystemProperties(Map<String, String> systemProperties) {
|
||||||
|
if (systemProperties != null) {
|
||||||
|
systemProperties.entrySet().stream().map((e) -> SystemPropertyFormatter.format(e.getKey(), e.getValue()))
|
||||||
|
.forEach(this.options::add);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandLineBuilder withClasspath(URL... elements) {
|
||||||
|
this.classpathElements.addAll(Arrays.asList(elements));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandLineBuilder withArguments(String... arguments) {
|
||||||
|
if (arguments != null) {
|
||||||
|
this.arguments.addAll(Arrays.stream(arguments).filter(Objects::nonNull).toList());
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> build() {
|
||||||
|
List<String> commandLine = new ArrayList<>();
|
||||||
|
if (!this.options.isEmpty()) {
|
||||||
|
commandLine.addAll(this.options);
|
||||||
|
}
|
||||||
|
if (!this.classpathElements.isEmpty()) {
|
||||||
|
commandLine.add("-cp");
|
||||||
|
commandLine.add(ClasspathBuilder.build(this.classpathElements));
|
||||||
|
}
|
||||||
|
commandLine.add(this.mainClass);
|
||||||
|
if (!this.arguments.isEmpty()) {
|
||||||
|
commandLine.addAll(this.arguments);
|
||||||
|
}
|
||||||
|
return commandLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ClasspathBuilder {
|
||||||
|
|
||||||
|
static String build(List<URL> classpathElements) {
|
||||||
|
StringBuilder classpath = new StringBuilder();
|
||||||
|
for (URL element : classpathElements) {
|
||||||
|
if (classpath.length() > 0) {
|
||||||
|
classpath.append(File.pathSeparator);
|
||||||
|
}
|
||||||
|
classpath.append(toFile(element));
|
||||||
|
}
|
||||||
|
return classpath.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static File toFile(URL element) {
|
||||||
|
try {
|
||||||
|
return new File(element.toURI());
|
||||||
|
}
|
||||||
|
catch (URISyntaxException ex) {
|
||||||
|
throw new IllegalArgumentException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format System properties.
|
||||||
|
*/
|
||||||
|
private static class SystemPropertyFormatter {
|
||||||
|
|
||||||
|
static String format(String key, String value) {
|
||||||
|
if (key == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
if (value == null || value.isEmpty()) {
|
||||||
|
return String.format("-D%s", key);
|
||||||
|
}
|
||||||
|
return String.format("-D%s=\"%s\"", key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2022 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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
|
||||||
|
*
|
||||||
|
* https://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.springframework.boot.maven;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.maven.execution.MavenSession;
|
||||||
|
import org.apache.maven.plugin.MojoExecutionException;
|
||||||
|
import org.apache.maven.toolchain.Toolchain;
|
||||||
|
import org.apache.maven.toolchain.ToolchainManager;
|
||||||
|
|
||||||
|
import org.springframework.boot.loader.tools.JavaExecutable;
|
||||||
|
import org.springframework.boot.loader.tools.RunProcess;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ease the execution of a Java process using Maven's toolchain support.
|
||||||
|
*
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
*/
|
||||||
|
class JavaProcessExecutor {
|
||||||
|
|
||||||
|
private static final int EXIT_CODE_SIGINT = 130;
|
||||||
|
|
||||||
|
private final MavenSession mavenSession;
|
||||||
|
|
||||||
|
private final ToolchainManager toolchainManager;
|
||||||
|
|
||||||
|
JavaProcessExecutor(MavenSession mavenSession, ToolchainManager toolchainManager) {
|
||||||
|
this.mavenSession = mavenSession;
|
||||||
|
this.toolchainManager = toolchainManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
int run(File workingDirectory, List<String> args, Map<String, String> environmentVariables)
|
||||||
|
throws MojoExecutionException {
|
||||||
|
RunProcess runProcess = new RunProcess(workingDirectory, getJavaExecutable());
|
||||||
|
try {
|
||||||
|
int exitCode = runProcess.run(true, args, environmentVariables);
|
||||||
|
if (!hasTerminatedSuccessfully(exitCode)) {
|
||||||
|
throw new MojoExecutionException("Process terminated with exit code: " + exitCode);
|
||||||
|
}
|
||||||
|
return exitCode;
|
||||||
|
}
|
||||||
|
catch (IOException ex) {
|
||||||
|
throw new MojoExecutionException("Process execution failed", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasTerminatedSuccessfully(int exitCode) {
|
||||||
|
return (exitCode == 0 || exitCode == EXIT_CODE_SIGINT);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getJavaExecutable() {
|
||||||
|
Toolchain toolchain = this.toolchainManager.getToolchainFromBuildContext("jdk", this.mavenSession);
|
||||||
|
String javaExecutable = (toolchain != null) ? toolchain.findTool("java") : null;
|
||||||
|
return (javaExecutable != null) ? javaExecutable : new JavaExecutable().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2022 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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
|
||||||
|
*
|
||||||
|
* https://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.springframework.boot.maven;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.apache.maven.plugin.MojoExecutionException;
|
||||||
|
|
||||||
|
import org.springframework.boot.loader.tools.MainClassFinder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a single Spring Boot Application class match based on directory.
|
||||||
|
*
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
* @see MainClassFinder
|
||||||
|
*/
|
||||||
|
abstract class SpringBootApplicationClassFinder {
|
||||||
|
|
||||||
|
private static final String SPRING_BOOT_APPLICATION_CLASS_NAME = "org.springframework.boot.autoconfigure.SpringBootApplication";
|
||||||
|
|
||||||
|
static String findSingleClass(File classesDirectory) throws MojoExecutionException {
|
||||||
|
try {
|
||||||
|
String mainClass = MainClassFinder.findSingleMainClass(classesDirectory,
|
||||||
|
SPRING_BOOT_APPLICATION_CLASS_NAME);
|
||||||
|
if (mainClass != null) {
|
||||||
|
return mainClass;
|
||||||
|
}
|
||||||
|
throw new MojoExecutionException("Unable to find a suitable main class, please add a 'mainClass' property");
|
||||||
|
}
|
||||||
|
catch (IOException ex) {
|
||||||
|
throw new MojoExecutionException(ex.getMessage(), ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2022 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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
|
||||||
|
*
|
||||||
|
* https://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.springframework.boot.maven;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.boot.maven.sample.ClassWithMainMethod;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link CommandLineBuilder}.
|
||||||
|
*
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
*/
|
||||||
|
class CommandLineBuilderTests {
|
||||||
|
|
||||||
|
public static final String CLASS_NAME = ClassWithMainMethod.class.getName();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void buildWithNullJvmArgumentsIsIgnored() {
|
||||||
|
assertThat(CommandLineBuilder.forMainClass(CLASS_NAME).withJvmArguments((String[]) null).build())
|
||||||
|
.containsExactly(CLASS_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void buildWithNullIntermediateJvmArgumentIsIgnored() {
|
||||||
|
assertThat(CommandLineBuilder.forMainClass(CLASS_NAME).withJvmArguments("-verbose:class", null, "-verbose:gc")
|
||||||
|
.build()).containsExactly("-verbose:class", "-verbose:gc", CLASS_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void buildWithJvmArgument() {
|
||||||
|
assertThat(CommandLineBuilder.forMainClass(CLASS_NAME).withJvmArguments("-verbose:class").build())
|
||||||
|
.containsExactly("-verbose:class", CLASS_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void buildWithNullSystemPropertyIsIgnored() {
|
||||||
|
assertThat(CommandLineBuilder.forMainClass(CLASS_NAME).withSystemProperties(null).build())
|
||||||
|
.containsExactly(CLASS_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void buildWithSystemProperty() {
|
||||||
|
assertThat(CommandLineBuilder.forMainClass(CLASS_NAME).withSystemProperties(Map.of("flag", "enabled")).build())
|
||||||
|
.containsExactly("-Dflag=\"enabled\"", CLASS_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void buildWithNullArgumentsIsIgnored() {
|
||||||
|
assertThat(CommandLineBuilder.forMainClass(CLASS_NAME).withArguments((String[]) null).build())
|
||||||
|
.containsExactly(CLASS_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void buildWithNullIntermediateArgumentIsIgnored() {
|
||||||
|
assertThat(CommandLineBuilder.forMainClass(CLASS_NAME).withArguments("--test", null, "--another").build())
|
||||||
|
.containsExactly(CLASS_NAME, "--test", "--another");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue