Skip to content

Commit 5a77122

Browse files
committed
Create classpath argfile on windows for run tasks
Closes gh-17766
1 parent a65e101 commit 5a77122

File tree

2 files changed

+110
-2
lines changed

2 files changed

+110
-2
lines changed

spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/AbstractRunMojo.java

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,14 @@
2020
import java.io.IOException;
2121
import java.net.MalformedURLException;
2222
import java.net.URL;
23+
import java.nio.file.Files;
24+
import java.nio.file.Path;
25+
import java.nio.file.StandardOpenOption;
2326
import java.util.ArrayList;
2427
import java.util.Arrays;
2528
import java.util.Collections;
2629
import java.util.List;
30+
import java.util.Locale;
2731
import java.util.Map;
2832
import java.util.Set;
2933
import java.util.stream.Collectors;
@@ -41,6 +45,7 @@
4145
import org.springframework.boot.loader.tools.FileUtils;
4246
import org.springframework.util.Assert;
4347
import org.springframework.util.ObjectUtils;
48+
import org.springframework.util.StringUtils;
4449

4550
/**
4651
* Base class to run a Spring Boot application.
@@ -50,6 +55,7 @@
5055
* @author David Liu
5156
* @author Daniel Young
5257
* @author Dmytro Nosan
58+
* @author Moritz Halbritter
5359
* @since 1.3.0
5460
* @see RunMojo
5561
* @see StartMojo
@@ -239,6 +245,10 @@ private void run(String startClassName) throws MojoExecutionException, MojoFailu
239245
JavaProcessExecutor processExecutor = new JavaProcessExecutor(this.session, this.toolchainManager);
240246
File workingDirectoryToUse = (this.workingDirectory != null) ? this.workingDirectory
241247
: this.project.getBasedir();
248+
if (getLog().isDebugEnabled()) {
249+
getLog().debug("Working directory: " + workingDirectoryToUse);
250+
getLog().debug("Java arguments: " + String.join(" ", args));
251+
}
242252
run(processExecutor, workingDirectoryToUse, args, determineEnvironmentVariables());
243253
}
244254

@@ -351,13 +361,40 @@ private void addClasspath(List<String> args) throws MojoExecutionException {
351361
getLog().debug("Classpath for forked process: " + classpath);
352362
}
353363
args.add("-cp");
354-
args.add(classpath.toString());
364+
if (needsClasspathArgFile()) {
365+
args.add("@" + writeClasspathArgFile(classpath.toString()));
366+
}
367+
else {
368+
args.add(classpath.toString());
369+
}
355370
}
356371
catch (Exception ex) {
357372
throw new MojoExecutionException("Could not build classpath", ex);
358373
}
359374
}
360375

376+
private boolean needsClasspathArgFile() {
377+
// Windows limits the maximum command length, so we use an argfile there
378+
return runsOnWindows();
379+
}
380+
381+
private boolean runsOnWindows() {
382+
String os = System.getProperty("os.name");
383+
if (!StringUtils.hasLength(os)) {
384+
if (getLog().isWarnEnabled()) {
385+
getLog().warn("System property os.name is not set");
386+
}
387+
return false;
388+
}
389+
return os.toLowerCase(Locale.ROOT).contains("win");
390+
}
391+
392+
private Path writeClasspathArgFile(String classpath) throws IOException {
393+
ArgFile argFile = ArgFile.create();
394+
argFile.write(classpath);
395+
return argFile.getPath();
396+
}
397+
361398
protected URL[] getClassPathUrls() throws MojoExecutionException {
362399
try {
363400
List<URL> urls = new ArrayList<>();
@@ -372,7 +409,6 @@ protected URL[] getClassPathUrls() throws MojoExecutionException {
372409
}
373410
}
374411

375-
@SuppressWarnings("removal")
376412
private void addAdditionalClasspathLocations(List<URL> urls) throws MalformedURLException {
377413
Assert.state(ObjectUtils.isEmpty(this.directories) || ObjectUtils.isEmpty(this.additionalClasspathElements),
378414
"Either additionalClasspathElements or directories (deprecated) should be set, not both");
@@ -437,4 +473,32 @@ static String format(String key, String value) {
437473

438474
}
439475

476+
static class ArgFile {
477+
478+
private final Path path;
479+
480+
ArgFile(Path path) {
481+
this.path = path;
482+
}
483+
484+
void write(String content) throws IOException {
485+
String escaped = escape(content);
486+
Files.writeString(this.path, "\"" + escaped + "\"", StandardOpenOption.APPEND);
487+
}
488+
489+
Path getPath() {
490+
return this.path;
491+
}
492+
493+
private String escape(String content) {
494+
return content.replace("\\", "\\\\");
495+
}
496+
497+
static ArgFile create() throws IOException {
498+
Path file = Files.createTempFile("spring-boot-", ".argfile");
499+
return new ArgFile(file);
500+
}
501+
502+
}
503+
440504
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright 2012-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.maven;
18+
19+
import java.io.IOException;
20+
import java.nio.charset.StandardCharsets;
21+
22+
import org.junit.jupiter.api.Test;
23+
24+
import org.springframework.boot.maven.AbstractRunMojo.ArgFile;
25+
26+
import static org.assertj.core.api.Assertions.assertThat;
27+
28+
/**
29+
* Tests for {@link AbstractRunMojo}.
30+
*
31+
* @author Moritz Halbritter
32+
*/
33+
class AbstractRunMojoTests {
34+
35+
@Test
36+
void argfileEscapesContent() throws IOException {
37+
ArgFile file = ArgFile.create();
38+
file.write("some \\ content");
39+
file.write("And even more content");
40+
assertThat(file.getPath()).content(StandardCharsets.UTF_8)
41+
.isEqualTo("\"some \\\\ content\"\"And even more content\"");
42+
}
43+
44+
}

0 commit comments

Comments
 (0)