Merge branch '3.4.x'
This commit is contained in:
commit
6076e9e2c1
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue