From 082c5859e88a41db2b9329e1a3e9ca9402f1c769 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Tue, 18 Feb 2025 15:25:04 -0800 Subject: [PATCH] Polish --- .../boot/maven/AbstractAotMojo.java | 25 ++- .../boot/maven/AbstractRunMojo.java | 6 +- .../springframework/boot/maven/ClassPath.java | 143 +++++++++++++ .../boot/maven/ClasspathBuilder.java | 196 ------------------ .../boot/maven/CommandLineBuilder.java | 5 +- .../boot/maven/ClassPathTests.java | 96 +++++++++ .../boot/maven/ClasspathBuilderTests.java | 102 --------- 7 files changed, 254 insertions(+), 319 deletions(-) create mode 100644 spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/ClassPath.java delete mode 100644 spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/ClasspathBuilder.java create mode 100644 spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/test/java/org/springframework/boot/maven/ClassPathTests.java delete mode 100644 spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/test/java/org/springframework/boot/maven/ClasspathBuilderTests.java diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/AbstractAotMojo.java b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/AbstractAotMojo.java index 24bf2f4f597..aae376b2965 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/AbstractAotMojo.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/AbstractAotMojo.java @@ -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 options = new ArrayList<>(); - options.add("-cp"); - options.add(ClasspathBuilder.forURLs(classPath).build().toString()); - options.add("-d"); - options.add(outputDirectory.toPath().toAbsolutePath().toString()); + List 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 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); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/AbstractRunMojo.java b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/AbstractRunMojo.java index 60529bbbd89..451a55f73c9 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/AbstractRunMojo.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/AbstractRunMojo.java @@ -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; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; @@ -346,12 +345,11 @@ public abstract class AbstractRunMojo extends AbstractDependencyFilterMojo { private void addClasspath(List 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()); + args.addAll(classpath.args(true)); } catch (Exception ex) { throw new MojoExecutionException("Could not build classpath", ex); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/ClassPath.java b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/ClassPath.java new file mode 100644 index 00000000000..7c4fe76ffec --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/ClassPath.java @@ -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 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 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 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 getSystemProperty, List 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 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); + } + } + +} diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/ClasspathBuilder.java b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/ClasspathBuilder.java deleted file mode 100644 index 24263f73e3f..00000000000 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/ClasspathBuilder.java +++ /dev/null @@ -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 urls; - - protected ClasspathBuilder(List 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 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 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 elements; - - private Classpath(String argument, List 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 elements() { - return this.elements.stream(); - } - - @Override - public String toString() { - return elements().map(Path::toString).collect(Collectors.joining(File.pathSeparator)); - } - - } - -} diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/CommandLineBuilder.java b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/CommandLineBuilder.java index 1e09773bd45..2c207eb1b52 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/CommandLineBuilder.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/CommandLineBuilder.java @@ -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); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/test/java/org/springframework/boot/maven/ClassPathTests.java b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/test/java/org/springframework/boot/maven/ClassPathTests.java new file mode 100644 index 00000000000..b06c06ea4b0 --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/test/java/org/springframework/boot/maven/ClassPathTests.java @@ -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 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 onWindows() { + return Map.of("os.name", "windows")::get; + } + + private UnaryOperator onLinux() { + return Map.of("os.name", "linux")::get; + } + +} diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/test/java/org/springframework/boot/maven/ClasspathBuilderTests.java b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/test/java/org/springframework/boot/maven/ClasspathBuilderTests.java deleted file mode 100644 index 39e4b1cdc22..00000000000 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/test/java/org/springframework/boot/maven/ClasspathBuilderTests.java +++ /dev/null @@ -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); - } - - } - -}