Merge branch '3.3.x' into 3.4.x
Closes gh-44328
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2012-2023 the original author or authors.
|
* Copyright 2012-2025 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -46,8 +46,6 @@ import org.apache.maven.plugins.annotations.Parameter;
|
|||||||
import org.apache.maven.shared.artifact.filter.collection.ArtifactsFilter;
|
import org.apache.maven.shared.artifact.filter.collection.ArtifactsFilter;
|
||||||
import org.apache.maven.toolchain.ToolchainManager;
|
import org.apache.maven.toolchain.ToolchainManager;
|
||||||
|
|
||||||
import org.springframework.boot.maven.CommandLineBuilder.ClasspathBuilder;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract base class for AOT processing MOJOs.
|
* Abstract base class for AOT processing MOJOs.
|
||||||
*
|
*
|
||||||
@@ -149,7 +147,7 @@ public abstract class AbstractAotMojo extends AbstractDependencyFilterMojo {
|
|||||||
JavaCompilerPluginConfiguration compilerConfiguration = new JavaCompilerPluginConfiguration(this.project);
|
JavaCompilerPluginConfiguration compilerConfiguration = new JavaCompilerPluginConfiguration(this.project);
|
||||||
List<String> options = new ArrayList<>();
|
List<String> options = new ArrayList<>();
|
||||||
options.add("-cp");
|
options.add("-cp");
|
||||||
options.add(ClasspathBuilder.build(Arrays.asList(classPath)));
|
options.add(ClasspathBuilder.forURLs(classPath).build().argument());
|
||||||
options.add("-d");
|
options.add("-d");
|
||||||
options.add(outputDirectory.toPath().toAbsolutePath().toString());
|
options.add(outputDirectory.toPath().toAbsolutePath().toString());
|
||||||
String releaseVersion = compilerConfiguration.getReleaseVersion();
|
String releaseVersion = compilerConfiguration.getReleaseVersion();
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2012-2024 the original author or authors.
|
* Copyright 2012-2025 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -20,15 +20,10 @@ import java.io.File;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.nio.charset.Charset;
|
|
||||||
import java.nio.charset.UnsupportedCharsetException;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@@ -44,7 +39,7 @@ import org.apache.maven.project.MavenProject;
|
|||||||
import org.apache.maven.toolchain.ToolchainManager;
|
import org.apache.maven.toolchain.ToolchainManager;
|
||||||
|
|
||||||
import org.springframework.boot.loader.tools.FileUtils;
|
import org.springframework.boot.loader.tools.FileUtils;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.boot.maven.ClasspathBuilder.Classpath;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class to run a Spring Boot application.
|
* Base class to run a Spring Boot application.
|
||||||
@@ -338,45 +333,19 @@ public abstract class AbstractRunMojo extends AbstractDependencyFilterMojo {
|
|||||||
|
|
||||||
private void addClasspath(List<String> args) throws MojoExecutionException {
|
private void addClasspath(List<String> args) throws MojoExecutionException {
|
||||||
try {
|
try {
|
||||||
StringBuilder classpath = new StringBuilder();
|
Classpath classpath = ClasspathBuilder.forURLs(getClassPathUrls()).build();
|
||||||
for (URL ele : getClassPathUrls()) {
|
|
||||||
if (!classpath.isEmpty()) {
|
|
||||||
classpath.append(File.pathSeparator);
|
|
||||||
}
|
|
||||||
classpath.append(new File(ele.toURI()));
|
|
||||||
}
|
|
||||||
if (getLog().isDebugEnabled()) {
|
if (getLog().isDebugEnabled()) {
|
||||||
getLog().debug("Classpath for forked process: " + classpath);
|
getLog().debug("Classpath for forked process: "
|
||||||
|
+ classpath.elements().map(Object::toString).collect(Collectors.joining(File.separator)));
|
||||||
}
|
}
|
||||||
args.add("-cp");
|
args.add("-cp");
|
||||||
if (needsClasspathArgFile()) {
|
args.add(classpath.argument());
|
||||||
args.add("@" + ArgFile.create(classpath).path());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
args.add(classpath.toString());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (Exception ex) {
|
catch (Exception ex) {
|
||||||
throw new MojoExecutionException("Could not build classpath", ex);
|
throw new MojoExecutionException("Could not build classpath", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean needsClasspathArgFile() {
|
|
||||||
// Windows limits the maximum command length, so we use an argfile there
|
|
||||||
return runsOnWindows();
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean runsOnWindows() {
|
|
||||||
String os = System.getProperty("os.name");
|
|
||||||
if (!StringUtils.hasLength(os)) {
|
|
||||||
if (getLog().isWarnEnabled()) {
|
|
||||||
getLog().warn("System property os.name is not set");
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return os.toLowerCase(Locale.ROOT).contains("win");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected URL[] getClassPathUrls() throws MojoExecutionException {
|
protected URL[] getClassPathUrls() throws MojoExecutionException {
|
||||||
try {
|
try {
|
||||||
List<URL> urls = new ArrayList<>();
|
List<URL> urls = new ArrayList<>();
|
||||||
@@ -451,37 +420,4 @@ public abstract class AbstractRunMojo extends AbstractDependencyFilterMojo {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
record ArgFile(Path path) {
|
|
||||||
|
|
||||||
private void write(CharSequence content) throws IOException {
|
|
||||||
Files.writeString(this.path, "\"" + escape(content) + "\"", getCharset());
|
|
||||||
}
|
|
||||||
|
|
||||||
private Charset getCharset() {
|
|
||||||
String nativeEncoding = System.getProperty("native.encoding");
|
|
||||||
if (nativeEncoding == null) {
|
|
||||||
return Charset.defaultCharset();
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return Charset.forName(nativeEncoding);
|
|
||||||
}
|
|
||||||
catch (UnsupportedCharsetException ex) {
|
|
||||||
return Charset.defaultCharset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String escape(CharSequence content) {
|
|
||||||
return content.toString().replace("\\", "\\\\");
|
|
||||||
}
|
|
||||||
|
|
||||||
static ArgFile create(CharSequence content) throws IOException {
|
|
||||||
Path tempFile = Files.createTempFile("spring-boot-", ".argfile");
|
|
||||||
tempFile.toFile().deleteOnExit();
|
|
||||||
ArgFile argFile = new ArgFile(tempFile);
|
|
||||||
argFile.write(content);
|
|
||||||
return argFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,177 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2025 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.net.URISyntaxException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.nio.charset.UnsupportedCharsetException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.springframework.util.ObjectUtils;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class to build the -cp (classpath) argument of a java process.
|
||||||
|
*
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
* @author Dmytro Nosan
|
||||||
|
*/
|
||||||
|
class ClasspathBuilder {
|
||||||
|
|
||||||
|
private final List<URL> urls;
|
||||||
|
|
||||||
|
protected ClasspathBuilder(List<URL> urls) {
|
||||||
|
this.urls = urls;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a classpath string or an argument file representing the classpath, depending
|
||||||
|
* on the operating system.
|
||||||
|
* @param urls an array of {@link URL} representing the elements of the classpath
|
||||||
|
* @return the classpath; on Windows, the path to an argument file is returned,
|
||||||
|
* prefixed with '@'
|
||||||
|
*/
|
||||||
|
static ClasspathBuilder forURLs(List<URL> urls) {
|
||||||
|
return new ClasspathBuilder(new ArrayList<>(urls));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a classpath string or an argument file representing the classpath, depending
|
||||||
|
* on the operating system.
|
||||||
|
* @param urls an array of {@link URL} representing the elements of the classpath
|
||||||
|
* @return the classpath; on Windows, the path to an argument file is returned,
|
||||||
|
* prefixed with '@'
|
||||||
|
*/
|
||||||
|
static ClasspathBuilder forURLs(URL... urls) {
|
||||||
|
return new ClasspathBuilder(Arrays.asList(urls));
|
||||||
|
}
|
||||||
|
|
||||||
|
Classpath build() {
|
||||||
|
if (ObjectUtils.isEmpty(this.urls)) {
|
||||||
|
return new Classpath("", Collections.emptyList());
|
||||||
|
}
|
||||||
|
if (this.urls.size() == 1) {
|
||||||
|
Path file = toFile(this.urls.get(0));
|
||||||
|
return new Classpath(file.toString(), List.of(file));
|
||||||
|
}
|
||||||
|
List<Path> files = this.urls.stream().map(ClasspathBuilder::toFile).toList();
|
||||||
|
String argument = files.stream().map(Object::toString).collect(Collectors.joining(File.pathSeparator));
|
||||||
|
if (needsClasspathArgFile()) {
|
||||||
|
argument = createArgFile(argument);
|
||||||
|
}
|
||||||
|
return new Classpath(argument, files);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean needsClasspathArgFile() {
|
||||||
|
String os = System.getProperty("os.name");
|
||||||
|
if (!StringUtils.hasText(os)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Windows limits the maximum command length, so we use an argfile
|
||||||
|
return os.toLowerCase(Locale.ROOT).contains("win");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a temporary file with the given {@code} classpath. Return a suitable
|
||||||
|
* argument to load the file, that is the full path prefixed by {@code @}.
|
||||||
|
* @param classpath the classpath to use
|
||||||
|
* @return a suitable argument for the classpath using a file
|
||||||
|
*/
|
||||||
|
private String createArgFile(String classpath) {
|
||||||
|
try {
|
||||||
|
return "@" + writeClasspathToFile(classpath);
|
||||||
|
}
|
||||||
|
catch (IOException ex) {
|
||||||
|
return classpath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Path writeClasspathToFile(CharSequence classpath) throws IOException {
|
||||||
|
Path tempFile = Files.createTempFile("spring-boot-", ".argfile");
|
||||||
|
tempFile.toFile().deleteOnExit();
|
||||||
|
Files.writeString(tempFile, "\"" + escape(classpath) + "\"", getCharset());
|
||||||
|
return tempFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Charset getCharset() {
|
||||||
|
String nativeEncoding = System.getProperty("native.encoding");
|
||||||
|
if (nativeEncoding == null) {
|
||||||
|
return Charset.defaultCharset();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return Charset.forName(nativeEncoding);
|
||||||
|
}
|
||||||
|
catch (UnsupportedCharsetException ex) {
|
||||||
|
return Charset.defaultCharset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String escape(CharSequence content) {
|
||||||
|
return content.toString().replace("\\", "\\\\");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Path toFile(URL url) {
|
||||||
|
try {
|
||||||
|
return Paths.get(url.toURI());
|
||||||
|
}
|
||||||
|
catch (URISyntaxException ex) {
|
||||||
|
throw new IllegalArgumentException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static final class Classpath {
|
||||||
|
|
||||||
|
private final String argument;
|
||||||
|
|
||||||
|
private final List<Path> elements;
|
||||||
|
|
||||||
|
private Classpath(String argument, List<Path> elements) {
|
||||||
|
this.argument = argument;
|
||||||
|
this.elements = elements;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the {@code -cp} argument value.
|
||||||
|
* @return the argument to use
|
||||||
|
*/
|
||||||
|
String argument() {
|
||||||
|
return this.argument;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the classpath elements.
|
||||||
|
* @return the JAR files to use
|
||||||
|
*/
|
||||||
|
Stream<Path> elements() {
|
||||||
|
return this.elements.stream();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2012-2024 the original author or authors.
|
* Copyright 2012-2025 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -16,8 +16,6 @@
|
|||||||
|
|
||||||
package org.springframework.boot.maven;
|
package org.springframework.boot.maven;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.net.URISyntaxException;
|
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@@ -84,7 +82,7 @@ final class CommandLineBuilder {
|
|||||||
}
|
}
|
||||||
if (!this.classpathElements.isEmpty()) {
|
if (!this.classpathElements.isEmpty()) {
|
||||||
commandLine.add("-cp");
|
commandLine.add("-cp");
|
||||||
commandLine.add(ClasspathBuilder.build(this.classpathElements));
|
commandLine.add(ClasspathBuilder.forURLs(this.classpathElements).build().argument());
|
||||||
}
|
}
|
||||||
commandLine.add(this.mainClass);
|
commandLine.add(this.mainClass);
|
||||||
if (!this.arguments.isEmpty()) {
|
if (!this.arguments.isEmpty()) {
|
||||||
@@ -93,30 +91,6 @@ final class CommandLineBuilder {
|
|||||||
return commandLine;
|
return commandLine;
|
||||||
}
|
}
|
||||||
|
|
||||||
static class ClasspathBuilder {
|
|
||||||
|
|
||||||
static String build(List<URL> classpathElements) {
|
|
||||||
StringBuilder classpath = new StringBuilder();
|
|
||||||
for (URL element : classpathElements) {
|
|
||||||
if (!classpath.isEmpty()) {
|
|
||||||
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.
|
* Format System properties.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,41 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2012-2024 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.IOException;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import org.springframework.boot.maven.AbstractRunMojo.ArgFile;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests for {@link AbstractRunMojo}.
|
|
||||||
*
|
|
||||||
* @author Moritz Halbritter
|
|
||||||
*/
|
|
||||||
class AbstractRunMojoTests {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void argfileEscapesContent() throws IOException {
|
|
||||||
ArgFile file = ArgFile.create("some \\ content");
|
|
||||||
assertThat(file.path()).content(StandardCharsets.UTF_8).isEqualTo("\"some \\\\ content\"");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,157 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2025 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.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Nested;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.condition.DisabledOnOs;
|
||||||
|
import org.junit.jupiter.api.condition.EnabledOnOs;
|
||||||
|
import org.junit.jupiter.api.condition.OS;
|
||||||
|
import org.junit.jupiter.api.io.TempDir;
|
||||||
|
|
||||||
|
import org.springframework.boot.maven.ClasspathBuilder.Classpath;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link ClasspathBuilder}.
|
||||||
|
*
|
||||||
|
* @author Dmytro Nosan
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
*/
|
||||||
|
class ClasspathBuilderTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisabledOnOs(OS.WINDOWS)
|
||||||
|
void buildWithMultipleClassPathURLs(@TempDir Path tempDir) throws Exception {
|
||||||
|
Path file = tempDir.resolve("test.jar");
|
||||||
|
Path file1 = tempDir.resolve("test1.jar");
|
||||||
|
assertThat(ClasspathBuilder.forURLs(file.toUri().toURL(), file1.toUri().toURL()).build().argument())
|
||||||
|
.isEqualTo(file + File.pathSeparator + file1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@EnabledOnOs(OS.WINDOWS)
|
||||||
|
void buildWithMultipleClassPathURLsOnWindows(@TempDir Path tempDir) throws Exception {
|
||||||
|
Path file = tempDir.resolve("test.jar");
|
||||||
|
Path file1 = tempDir.resolve("test1.jar");
|
||||||
|
String classpath = ClasspathBuilder.forURLs(file.toUri().toURL(), file1.toUri().toURL()).build().argument();
|
||||||
|
assertThat(classpath).startsWith("@");
|
||||||
|
assertThat(Paths.get(classpath.substring(1)))
|
||||||
|
.hasContent("\"" + (file + File.pathSeparator + file1).replace("\\", "\\\\") + "\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
class WindowsTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void buildWithEmptyClassPath() throws MalformedURLException {
|
||||||
|
Classpath classpath = classPathBuilder().build();
|
||||||
|
assertThat(classpath.argument()).isEmpty();
|
||||||
|
assertThat(classpath.elements()).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void buildWithSingleClassPathURL(@TempDir Path tempDir) throws Exception {
|
||||||
|
Path file = tempDir.resolve("test.jar");
|
||||||
|
Classpath classpath = classPathBuilder(file).build();
|
||||||
|
assertThat(classpath.argument()).isEqualTo(file.toString());
|
||||||
|
assertThat(classpath.elements()).singleElement().isEqualTo(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void buildWithMultipleClassPathURLs(@TempDir Path tempDir) throws Exception {
|
||||||
|
Path file = tempDir.resolve("test.jar");
|
||||||
|
Path file2 = tempDir.resolve("test2.jar");
|
||||||
|
Classpath classpath = classPathBuilder(file, file2).build();
|
||||||
|
assertThat(classpath.argument()).startsWith("@");
|
||||||
|
assertThat(Paths.get(classpath.argument().substring(1)))
|
||||||
|
.hasContent("\"" + (file + File.pathSeparator + file2).replace("\\", "\\\\") + "\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
private ClasspathBuilder classPathBuilder(Path... files) throws MalformedURLException {
|
||||||
|
return new TestClasspathBuilder(true, files);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
class UnixTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void buildWithEmptyClassPath() throws MalformedURLException {
|
||||||
|
Classpath classpath = classPathBuilder().build();
|
||||||
|
assertThat(classpath.argument()).isEmpty();
|
||||||
|
assertThat(classpath.elements()).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void buildWithSingleClassPathURL(@TempDir Path tempDir) throws Exception {
|
||||||
|
Path file = tempDir.resolve("test.jar");
|
||||||
|
Classpath classpath = classPathBuilder(file).build();
|
||||||
|
assertThat(classpath.argument()).isEqualTo(file.toString());
|
||||||
|
assertThat(classpath.elements()).singleElement().isEqualTo(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void buildWithMultipleClassPathURLs(@TempDir Path tempDir) throws Exception {
|
||||||
|
Path file = tempDir.resolve("test.jar");
|
||||||
|
Path file2 = tempDir.resolve("test2.jar");
|
||||||
|
Classpath classpath = classPathBuilder(file, file2).build();
|
||||||
|
assertThat(classpath.argument()).doesNotStartWith("@")
|
||||||
|
.isEqualTo((file + File.pathSeparator + file2).replace("\\", "\\\\"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private ClasspathBuilder classPathBuilder(Path... files) throws MalformedURLException {
|
||||||
|
return new TestClasspathBuilder(false, files);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class TestClasspathBuilder extends ClasspathBuilder {
|
||||||
|
|
||||||
|
private final boolean needsClasspathArgFile;
|
||||||
|
|
||||||
|
protected TestClasspathBuilder(boolean needsClasspathArgFile, Path... files) throws MalformedURLException {
|
||||||
|
super(toURLs(files));
|
||||||
|
this.needsClasspathArgFile = needsClasspathArgFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<URL> toURLs(Path... files) throws MalformedURLException {
|
||||||
|
List<URL> urls = new ArrayList<>();
|
||||||
|
for (Path file : files) {
|
||||||
|
urls.add(file.toUri().toURL());
|
||||||
|
}
|
||||||
|
return urls;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean needsClasspathArgFile() {
|
||||||
|
return this.needsClasspathArgFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2012-2023 the original author or authors.
|
* Copyright 2012-2025 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -16,10 +16,25 @@
|
|||||||
|
|
||||||
package org.springframework.boot.maven;
|
package org.springframework.boot.maven;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.lang.management.ManagementFactory;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.condition.DisabledOnOs;
|
||||||
|
import org.junit.jupiter.api.condition.EnabledOnOs;
|
||||||
|
import org.junit.jupiter.api.condition.OS;
|
||||||
|
import org.junit.jupiter.api.io.TempDir;
|
||||||
|
|
||||||
|
import org.springframework.boot.loader.tools.JavaExecutable;
|
||||||
import org.springframework.boot.maven.sample.ClassWithMainMethod;
|
import org.springframework.boot.maven.sample.ClassWithMainMethod;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
@@ -76,4 +91,55 @@ class CommandLineBuilderTests {
|
|||||||
.containsExactly(CLASS_NAME, "--test", "--another");
|
.containsExactly(CLASS_NAME, "--test", "--another");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisabledOnOs(OS.WINDOWS)
|
||||||
|
void buildWithClassPath(@TempDir Path tempDir) throws Exception {
|
||||||
|
Path file = tempDir.resolve("test.jar");
|
||||||
|
Path file1 = tempDir.resolve("test1.jar");
|
||||||
|
assertThat(CommandLineBuilder.forMainClass(CLASS_NAME)
|
||||||
|
.withClasspath(file.toUri().toURL(), file1.toUri().toURL())
|
||||||
|
.build()).containsExactly("-cp", file + File.pathSeparator + file1, CLASS_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@EnabledOnOs(OS.WINDOWS)
|
||||||
|
void buildWithClassPathOnWindows(@TempDir Path tempDir) throws Exception {
|
||||||
|
Path file = tempDir.resolve("test.jar");
|
||||||
|
Path file1 = tempDir.resolve("test1.jar");
|
||||||
|
List<String> args = CommandLineBuilder.forMainClass(CLASS_NAME)
|
||||||
|
.withClasspath(file.toUri().toURL(), file1.toUri().toURL())
|
||||||
|
.build();
|
||||||
|
assertThat(args).hasSize(3);
|
||||||
|
assertThat(args.get(0)).isEqualTo("-cp");
|
||||||
|
assertThat(args.get(1)).startsWith("@");
|
||||||
|
assertThat(args.get(2)).isEqualTo(CLASS_NAME);
|
||||||
|
assertThat(Paths.get(args.get(1).substring(1)))
|
||||||
|
.hasContent("\"" + (file + File.pathSeparator + file1).replace("\\", "\\\\") + "\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void buildAndRunWithLongClassPath() throws IOException, InterruptedException {
|
||||||
|
URL[] urls = Arrays.stream(ManagementFactory.getRuntimeMXBean().getClassPath().split(File.pathSeparator))
|
||||||
|
.map(this::toURL)
|
||||||
|
.toArray(URL[]::new);
|
||||||
|
List<String> command = CommandLineBuilder.forMainClass(ClassWithMainMethod.class.getName())
|
||||||
|
.withClasspath(urls)
|
||||||
|
.build();
|
||||||
|
ProcessBuilder pb = new JavaExecutable().processBuilder(command.toArray(new String[0]));
|
||||||
|
Process process = pb.start();
|
||||||
|
assertThat(process.waitFor()).isEqualTo(0);
|
||||||
|
try (InputStream inputStream = process.getInputStream()) {
|
||||||
|
assertThat(inputStream).hasContent("Hello World");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private URL toURL(String path) {
|
||||||
|
try {
|
||||||
|
return Paths.get(path).toUri().toURL();
|
||||||
|
}
|
||||||
|
catch (MalformedURLException ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user