Merge branch '3.3.x' into 3.4.x

This commit is contained in:
Phillip Webb 2025-02-18 17:29:31 -08:00
commit 661fcfe033
7 changed files with 307 additions and 351 deletions

View File

@ -145,32 +145,31 @@ public abstract class AbstractAotMojo extends AbstractDependencyFilterMojo {
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
try (StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null)) {
JavaCompilerPluginConfiguration compilerConfiguration = new JavaCompilerPluginConfiguration(this.project);
List<String> options = new ArrayList<>();
options.add("-cp");
options.add(ClasspathBuilder.forURLs(classPath).build().toString());
options.add("-d");
options.add(outputDirectory.toPath().toAbsolutePath().toString());
List<String> args = new ArrayList<>();
args.addAll(ClassPath.of(classPath).args(false));
args.add("-d");
args.add(outputDirectory.toPath().toAbsolutePath().toString());
String releaseVersion = compilerConfiguration.getReleaseVersion();
if (releaseVersion != null) {
options.add("--release");
options.add(releaseVersion);
args.add("--release");
args.add(releaseVersion);
}
else {
String source = compilerConfiguration.getSourceMajorVersion();
if (source != null) {
options.add("--source");
options.add(source);
args.add("--source");
args.add(source);
}
String target = compilerConfiguration.getTargetMajorVersion();
if (target != null) {
options.add("--target");
options.add(target);
args.add("--target");
args.add(target);
}
}
options.addAll(new RunArguments(this.compilerArguments).getArgs());
args.addAll(new RunArguments(this.compilerArguments).getArgs());
Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjectsFromPaths(sourceFiles);
Errors errors = new Errors();
CompilationTask task = compiler.getTask(null, fileManager, errors, options, null, compilationUnits);
CompilationTask task = compiler.getTask(null, fileManager, errors, args, null, compilationUnits);
boolean result = task.call();
if (!result || errors.hasReportedErrors()) {
throw new IllegalStateException("Unable to compile generated source" + errors);

View File

@ -39,7 +39,6 @@ import org.apache.maven.project.MavenProject;
import org.apache.maven.toolchain.ToolchainManager;
import org.springframework.boot.loader.tools.FileUtils;
import org.springframework.boot.maven.ClasspathBuilder.Classpath;
/**
* Base class to run a Spring Boot application.
@ -58,6 +57,7 @@ public abstract class AbstractRunMojo extends AbstractDependencyFilterMojo {
/**
* The Maven project.
*
* @since 1.0.0
*/
@Parameter(defaultValue = "${project}", readonly = true, required = true)
@ -65,6 +65,7 @@ public abstract class AbstractRunMojo extends AbstractDependencyFilterMojo {
/**
* The current Maven session. This is used for toolchain manager API calls.
*
* @since 2.3.0
*/
@Parameter(defaultValue = "${session}", readonly = true)
@ -72,17 +73,20 @@ public abstract class AbstractRunMojo extends AbstractDependencyFilterMojo {
/**
* The toolchain manager to use to locate a custom JDK.
*
* @since 2.3.0
*/
@Component
private ToolchainManager toolchainManager;
/**
* Add maven resources to the classpath directly, this allows live in-place editing of
* resources. Duplicate resources are removed from {@code target/classes} to prevent
* them from appearing twice if {@code ClassLoader.getResources()} is called. Please
* consider adding {@code spring-boot-devtools} to your project instead as it provides
* this feature and many more.
* Add maven resources to the classpath directly, this allows live in-place
* editing of resources. Duplicate resources are removed from
* {@code target/classes} to prevent them from appearing twice if
* {@code ClassLoader.getResources()} is called. Please consider adding
* {@code spring-boot-devtools} to your project instead as it provides this
* feature and many more.
*
* @since 1.0.0
*/
@Parameter(property = "spring-boot.run.addResources", defaultValue = "false")
@ -90,6 +94,7 @@ public abstract class AbstractRunMojo extends AbstractDependencyFilterMojo {
/**
* Path to agent jars.
*
* @since 2.2.0
*/
@Parameter(property = "spring-boot.run.agents")
@ -97,22 +102,26 @@ public abstract class AbstractRunMojo extends AbstractDependencyFilterMojo {
/**
* Flag to say that the agent requires -noverify.
*
* @since 1.0.0
*/
@Parameter(property = "spring-boot.run.noverify")
private boolean noverify = false;
/**
* Current working directory to use for the application. If not specified, basedir
* will be used.
* Current working directory to use for the application. If not specified,
* basedir will be used.
*
* @since 1.5.0
*/
@Parameter(property = "spring-boot.run.workingDirectory")
private File workingDirectory;
/**
* JVM arguments that should be associated with the forked process used to run the
* application. On command line, make sure to wrap multiple values between quotes.
* JVM arguments that should be associated with the forked process used to run
* the application. On command line, make sure to wrap multiple values between
* quotes.
*
* @since 1.1.0
*/
@Parameter(property = "spring-boot.run.jvmArguments")
@ -120,14 +129,16 @@ public abstract class AbstractRunMojo extends AbstractDependencyFilterMojo {
/**
* List of JVM system properties to pass to the process.
*
* @since 2.1.0
*/
@Parameter
private Map<String, String> systemPropertyVariables;
/**
* List of Environment variables that should be associated with the forked process
* used to run the application.
* List of Environment variables that should be associated with the forked
* process used to run the application.
*
* @since 2.1.0
*/
@Parameter
@ -135,6 +146,7 @@ public abstract class AbstractRunMojo extends AbstractDependencyFilterMojo {
/**
* Arguments that should be passed to the application.
*
* @since 1.0.0
*/
@Parameter
@ -142,8 +154,9 @@ public abstract class AbstractRunMojo extends AbstractDependencyFilterMojo {
/**
* Arguments from the command line that should be passed to the application. Use
* spaces to separate multiple arguments and make sure to wrap multiple values between
* quotes. When specified, takes precedence over {@link #arguments}.
* spaces to separate multiple arguments and make sure to wrap multiple values
* between quotes. When specified, takes precedence over {@link #arguments}.
*
* @since 2.2.3
*/
@Parameter(property = "spring-boot.run.arguments")
@ -151,32 +164,36 @@ public abstract class AbstractRunMojo extends AbstractDependencyFilterMojo {
/**
* The spring profiles to activate. Convenience shortcut of specifying the
* 'spring.profiles.active' argument. On command line use commas to separate multiple
* profiles.
* 'spring.profiles.active' argument. On command line use commas to separate
* multiple profiles.
*
* @since 1.3.0
*/
@Parameter(property = "spring-boot.run.profiles")
private String[] profiles;
/**
* The name of the main class. If not specified the first compiled class found that
* contains a 'main' method will be used.
* The name of the main class. If not specified the first compiled class found
* that contains a 'main' method will be used.
*
* @since 1.0.0
*/
@Parameter(property = "spring-boot.run.main-class")
private String mainClass;
/**
* Additional classpath elements that should be added to the classpath. An element can
* be a directory with classes and resources or a jar file.
* Additional classpath elements that should be added to the classpath. An
* element can be a directory with classes and resources or a jar file.
*
* @since 3.2.0
*/
@Parameter(property = "spring-boot.run.additional-classpath-elements")
private String[] additionalClasspathElements;
/**
* Directory containing the classes and resource files that should be used to run the
* application.
* Directory containing the classes and resource files that should be used to
* run the application.
*
* @since 1.0.0
*/
@Parameter(defaultValue = "${project.build.outputDirectory}", required = true)
@ -184,6 +201,7 @@ public abstract class AbstractRunMojo extends AbstractDependencyFilterMojo {
/**
* Skip the execution.
*
* @since 1.3.2
*/
@Parameter(property = "spring-boot.run.skip", defaultValue = "false")
@ -206,9 +224,10 @@ public abstract class AbstractRunMojo extends AbstractDependencyFilterMojo {
}
/**
* Returns the directories that contain the application's classes and resources. When
* the application's main class has not been configured, each directory is searched in
* turn for an appropriate main class.
* Returns the directories that contain the application's classes and resources.
* When the application's main class has not been configured, each directory is
* searched in turn for an appropriate main class.
*
* @return the directories that contain the application's classes and resources
* @since 3.1.0
*/
@ -237,6 +256,7 @@ public abstract class AbstractRunMojo extends AbstractDependencyFilterMojo {
/**
* Run the application.
*
* @param processExecutor the {@link JavaProcessExecutor} to use
* @param workingDirectory the working directory of the forked JVM
* @param args the arguments (JVM arguments and application arguments)
@ -250,6 +270,7 @@ public abstract class AbstractRunMojo extends AbstractDependencyFilterMojo {
/**
* Resolve the application arguments to use.
*
* @return a {@link RunArguments} defining the application arguments
*/
protected RunArguments resolveApplicationArguments() {
@ -261,6 +282,7 @@ public abstract class AbstractRunMojo extends AbstractDependencyFilterMojo {
/**
* Resolve the environment variables to use.
*
* @return an {@link EnvVariables} defining the environment variables
*/
protected EnvVariables resolveEnvVariables() {
@ -281,15 +303,15 @@ public abstract class AbstractRunMojo extends AbstractDependencyFilterMojo {
/**
* Resolve the JVM arguments to use.
*
* @return a {@link RunArguments} defining the JVM arguments
*/
protected RunArguments resolveJvmArguments() {
StringBuilder stringBuilder = new StringBuilder();
if (this.systemPropertyVariables != null) {
stringBuilder.append(this.systemPropertyVariables.entrySet()
.stream()
.map((e) -> SystemPropertyFormatter.format(e.getKey(), e.getValue()))
.collect(Collectors.joining(" ")));
stringBuilder.append(this.systemPropertyVariables.entrySet().stream()
.map((e) -> SystemPropertyFormatter.format(e.getKey(), e.getValue()))
.collect(Collectors.joining(" ")));
}
if (this.jvmArguments != null) {
stringBuilder.append(" ").append(this.jvmArguments);
@ -333,14 +355,12 @@ public abstract class AbstractRunMojo extends AbstractDependencyFilterMojo {
private void addClasspath(List<String> args) throws MojoExecutionException {
try {
Classpath classpath = ClasspathBuilder.forURLs(getClassPathUrls()).build();
ClassPath classpath = ClassPath.of(getClassPathUrls());
if (getLog().isDebugEnabled()) {
getLog().debug("Classpath for forked process: " + classpath);
}
args.add("-cp");
args.add(classpath.argument());
}
catch (Exception ex) {
args.addAll(classpath.args(true));
} catch (Exception ex) {
throw new MojoExecutionException("Could not build classpath", ex);
}
}
@ -353,8 +373,7 @@ public abstract class AbstractRunMojo extends AbstractDependencyFilterMojo {
addProjectClasses(urls);
addDependencies(urls);
return urls.toArray(new URL[0]);
}
catch (IOException ex) {
} catch (IOException ex) {
throw new MojoExecutionException("Unable to build classpath", ex);
}
}

View File

@ -0,0 +1,143 @@
/*
* 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.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.function.UnaryOperator;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import org.springframework.util.StringUtils;
/**
* Encapsulates a class path and allows argument parameters to be created. On Windows an
* argument file is used whenever possible since the maximum command line length is
* limited.
*
* @author Stephane Nicoll
* @author Dmytro Nosan
* @author Phillip Webb
*/
final class ClassPath {
private static final Collector<CharSequence, ?, String> JOIN_BY_PATH_SEPARATOR = Collectors
.joining(File.pathSeparator);
private final boolean preferArgFile;
private final String path;
private ClassPath(boolean preferArgFile, String path) {
this.preferArgFile = preferArgFile;
this.path = path;
}
/**
* Return the args to append to a java command line call (including {@code -cp}).
* @param allowArgFile if an arg file can be used
* @return the command line arguments
*/
List<String> args(boolean allowArgFile) {
return (!this.path.isEmpty()) ? List.of("-cp", classPathArg(allowArgFile)) : Collections.emptyList();
}
private String classPathArg(boolean allowArgFile) {
if (this.preferArgFile && allowArgFile) {
try {
return "@" + createArgFile();
}
catch (IOException ex) {
return this.path;
}
}
return this.path;
}
private Path createArgFile() throws IOException {
Path argFile = Files.createTempFile("spring-boot-", ".argfile");
argFile.toFile().deleteOnExit();
Files.writeString(argFile, "\"" + this.path.replace("\\", "\\\\") + "\"", charset());
return argFile;
}
private Charset charset() {
try {
String nativeEncoding = System.getProperty("native.encoding");
return (nativeEncoding != null) ? Charset.forName(nativeEncoding) : Charset.defaultCharset();
}
catch (UnsupportedCharsetException ex) {
return Charset.defaultCharset();
}
}
/**
* Factory method to create a {@link ClassPath} of the given URLs.
* @param urls the class path URLs
* @return a new {@link ClassPath} instance
*/
static ClassPath of(URL... urls) {
return of(Arrays.asList(urls));
}
/**
* Factory method to create a {@link ClassPath} of the given URLs.
* @param urls the class path URLs
* @return a new {@link ClassPath} instance
*/
static ClassPath of(List<URL> urls) {
return of(System::getProperty, urls);
}
/**
* Factory method to create a {@link ClassPath} of the given URLs.
* @param getSystemProperty {@link UnaryOperator} allowing access to system properties
* @param urls the class path URLs
* @return a new {@link ClassPath} instance
*/
static ClassPath of(UnaryOperator<String> getSystemProperty, List<URL> urls) {
boolean preferrArgFile = urls.size() > 1 && isWindows(getSystemProperty);
return new ClassPath(preferrArgFile,
urls.stream().map(ClassPath::toPathString).collect(JOIN_BY_PATH_SEPARATOR));
}
private static boolean isWindows(UnaryOperator<String> getSystemProperty) {
String os = getSystemProperty.apply("os.name");
return StringUtils.hasText(os) && os.toLowerCase(Locale.ROOT).contains("win");
}
private static String toPathString(URL url) {
try {
return Paths.get(url.toURI()).toString();
}
catch (URISyntaxException ex) {
throw new IllegalArgumentException(ex);
}
}
}

View File

@ -1,196 +0,0 @@
/*
* 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;
}
/**
* Creates a ClasspathBuilder instance using the specified list of URLs.
* @param urls a list of {@link URL} objects representing the elements of the
* classpath
* @return a new instance of {@code ClasspathBuilder}
*/
static ClasspathBuilder forURLs(List<URL> urls) {
return new ClasspathBuilder(new ArrayList<>(urls));
}
/**
* Creates a ClasspathBuilder instance using the specified array of URLs.
* @param urls an array of {@link URL} objects representing the elements of the
* classpath
* @return a new instance of {@code ClasspathBuilder}
*/
static ClasspathBuilder forURLs(URL... urls) {
return new ClasspathBuilder(Arrays.asList(urls));
}
/**
* Builds {@link Classpath} that containing a classpath argument and its corresponding
* classpath elements.
* @return a {@code Classpath}
*/
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);
}
/**
* Determines if an argument file should be used for the classpath based on the
* operating system. On Windows, argument files are used due to the command length
* limitation.
* @return {@code true} if an argument file is required for the classpath,
* {@code false} otherwise
*/
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);
}
}
/**
* Classpath consisting of a {@code -cp} argument and its associated elements.
*/
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; on Windows, the path to an argument file
* is returned, prefixed with '@'.
* @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();
}
@Override
public String toString() {
return elements().map(Path::toString).collect(Collectors.joining(File.pathSeparator));
}
}
}

View File

@ -80,10 +80,7 @@ final class CommandLineBuilder {
if (!this.options.isEmpty()) {
commandLine.addAll(this.options);
}
if (!this.classpathElements.isEmpty()) {
commandLine.add("-cp");
commandLine.add(ClasspathBuilder.forURLs(this.classpathElements).build().argument());
}
commandLine.addAll(ClassPath.of(this.classpathElements).args(true));
commandLine.add(this.mainClass);
if (!this.arguments.isEmpty()) {
commandLine.addAll(this.arguments);

View File

@ -0,0 +1,96 @@
/*
* 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.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.UnaryOperator;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Test for {@link ClassPath}.
*
* @author Dmytro Nosan
* @author Stephane Nicoll
* @author Phillip Webb
*/
class ClassPathTests {
@Test
void argsWhenNoClassPathReturnsEmptyList() {
assertThat(ClassPath.of(Collections.emptyList()).args(false)).isEmpty();
}
@Test
void argsWhenSingleUrlOnWindowsUsesPath(@TempDir Path temp) throws Exception {
Path path = temp.resolve("test.jar");
ClassPath classPath = ClassPath.of(onWindows(), List.of(path.toUri().toURL()));
assertThat(classPath.args(true)).containsExactly("-cp", path.toString());
}
@Test
void argsWhenSingleUrlNotOnWindowsUsesPath(@TempDir Path temp) throws Exception {
Path path = temp.resolve("test.jar");
ClassPath classPath = ClassPath.of(onLinux(), List.of(path.toUri().toURL()));
assertThat(classPath.args(true)).containsExactly("-cp", path.toString());
}
@Test
void argsWhenMultipleUrlsOnWindowsAndAllowedUsesArgFile(@TempDir Path temp) throws Exception {
Path path1 = temp.resolve("test1.jar");
Path path2 = temp.resolve("test2.jar");
ClassPath classPath = ClassPath.of(onWindows(), List.of(path1.toUri().toURL(), path2.toUri().toURL()));
List<String> args = classPath.args(true);
assertThat(args.get(0)).isEqualTo("-cp");
assertThat(args.get(1)).startsWith("@");
assertThat(Paths.get(args.get(1).substring(1)))
.hasContent("\"" + (path1 + File.pathSeparator + path2).replace("\\", "\\\\") + "\"");
}
@Test
void argsWhenMultipleUrlsOnWindowsAndNotAllowedUsesPath(@TempDir Path temp) throws Exception {
Path path1 = temp.resolve("test1.jar");
Path path2 = temp.resolve("test2.jar");
ClassPath classPath = ClassPath.of(onWindows(), List.of(path1.toUri().toURL(), path2.toUri().toURL()));
assertThat(classPath.args(false)).containsExactly("-cp", path1 + File.pathSeparator + path2);
}
@Test
void argsWhenMultipleUrlsNotOnWindowsUsesPath(@TempDir Path temp) throws Exception {
Path path1 = temp.resolve("test1.jar");
Path path2 = temp.resolve("test2.jar");
ClassPath classPath = ClassPath.of(onLinux(), List.of(path1.toUri().toURL(), path2.toUri().toURL()));
assertThat(classPath.args(true)).containsExactly("-cp", path1 + File.pathSeparator + path2);
}
private UnaryOperator<String> onWindows() {
return Map.of("os.name", "windows")::get;
}
private UnaryOperator<String> onLinux() {
return Map.of("os.name", "linux")::get;
}
}

View File

@ -1,102 +0,0 @@
/*
* 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.nio.file.Path;
import java.nio.file.Paths;
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 {
@Nested
@EnabledOnOs(OS.WINDOWS)
class WindowsTests {
@Test
void buildWithEmptyClassPath() {
Classpath classpath = ClasspathBuilder.forURLs().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.forURLs(file.toUri().toURL()).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 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
@DisabledOnOs(OS.WINDOWS)
class UnixTests {
@Test
void buildWithEmptyClassPath() {
Classpath classpath = ClasspathBuilder.forURLs().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.forURLs(file.toUri().toURL()).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 file1 = tempDir.resolve("test1.jar");
assertThat(ClasspathBuilder.forURLs(file.toUri().toURL(), file1.toUri().toURL()).build().argument())
.isEqualTo(file + File.pathSeparator + file1);
}
}
}