From a93784207e629e8e95a642a89c073bb8fec70d72 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Wed, 14 May 2014 13:26:11 +0200 Subject: [PATCH] Add jvmArguments property to maven plugin The maven plugin now forks a new process when it starts a boot app. This makes remote debugging of the app impossible without the ability to pass extra JVM arguments. This commit adds a "jvmArguments" attribute to the RunMojo that defines additional JVM arguments to set on the forked process. Fixes gh-848 --- .../src/it/run-jvmargs/pom.xml | 31 ++++++++ .../main/java/org/test/SampleApplication.java | 17 +++++ .../src/it/run-jvmargs/verify.groovy | 3 + .../springframework/boot/maven/RunMojo.java | 56 +++++++++++++- .../src/site/apt/examples/run-debug.apt.vm | 54 +++++++++++++ .../src/site/apt/index.apt | 2 + .../src/site/apt/usage.apt.vm | 4 + .../src/site/site.xml | 1 + .../boot/maven/RunMojoTests.java | 75 +++++++++++++++++++ 9 files changed, 239 insertions(+), 4 deletions(-) create mode 100644 spring-boot-tools/spring-boot-maven-plugin/src/it/run-jvmargs/pom.xml create mode 100644 spring-boot-tools/spring-boot-maven-plugin/src/it/run-jvmargs/src/main/java/org/test/SampleApplication.java create mode 100644 spring-boot-tools/spring-boot-maven-plugin/src/it/run-jvmargs/verify.groovy create mode 100644 spring-boot-tools/spring-boot-maven-plugin/src/site/apt/examples/run-debug.apt.vm create mode 100644 spring-boot-tools/spring-boot-maven-plugin/src/test/java/org/springframework/boot/maven/RunMojoTests.java diff --git a/spring-boot-tools/spring-boot-maven-plugin/src/it/run-jvmargs/pom.xml b/spring-boot-tools/spring-boot-maven-plugin/src/it/run-jvmargs/pom.xml new file mode 100644 index 00000000000..cff254f9395 --- /dev/null +++ b/spring-boot-tools/spring-boot-maven-plugin/src/it/run-jvmargs/pom.xml @@ -0,0 +1,31 @@ + + + 4.0.0 + org.springframework.boot.maven.it + run-jvmargs + 0.0.1.BUILD-SNAPSHOT + + UTF-8 + + + + + @project.groupId@ + @project.artifactId@ + @project.version@ + + + package + + run + + + -Dfoo="value 1" -Dbar=value2 + + + + + + + diff --git a/spring-boot-tools/spring-boot-maven-plugin/src/it/run-jvmargs/src/main/java/org/test/SampleApplication.java b/spring-boot-tools/spring-boot-maven-plugin/src/it/run-jvmargs/src/main/java/org/test/SampleApplication.java new file mode 100644 index 00000000000..437557e0f8c --- /dev/null +++ b/spring-boot-tools/spring-boot-maven-plugin/src/it/run-jvmargs/src/main/java/org/test/SampleApplication.java @@ -0,0 +1,17 @@ +package org.test; + +public class SampleApplication { + + public static void main(String[] args) { + String foo = System.getProperty("foo"); + if (!"value 1".equals(foo)) { + throw new IllegalStateException("foo system property mismatch (got [" + foo + "]"); + } + String bar = System.getProperty("bar"); + if (!"value2".equals(bar)) { + throw new IllegalStateException("bar system property mismatch (got [" + bar + "]"); + } + System.out.println("I haz been run"); + } + +} diff --git a/spring-boot-tools/spring-boot-maven-plugin/src/it/run-jvmargs/verify.groovy b/spring-boot-tools/spring-boot-maven-plugin/src/it/run-jvmargs/verify.groovy new file mode 100644 index 00000000000..841c4a97de5 --- /dev/null +++ b/spring-boot-tools/spring-boot-maven-plugin/src/it/run-jvmargs/verify.groovy @@ -0,0 +1,3 @@ +def file = new File(basedir, "build.log") +return file.text.contains("I haz been run") + diff --git a/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/RunMojo.java b/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/RunMojo.java index 141ebf714c2..fdfb1708195 100644 --- a/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/RunMojo.java +++ b/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/RunMojo.java @@ -23,6 +23,7 @@ import java.net.URL; import java.security.CodeSource; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Set; @@ -38,6 +39,8 @@ import org.apache.maven.plugins.annotations.ResolutionScope; import org.apache.maven.project.MavenProject; import org.apache.maven.shared.artifact.filter.collection.AbstractArtifactFeatureFilter; import org.apache.maven.shared.artifact.filter.collection.FilterArtifacts; +import org.codehaus.plexus.util.cli.CommandLineUtils; + import org.springframework.boot.loader.tools.FileUtils; import org.springframework.boot.loader.tools.JavaExecutable; import org.springframework.boot.loader.tools.MainClassFinder; @@ -87,6 +90,15 @@ public class RunMojo extends AbstractDependencyFilterMojo { @Parameter(property = "run.noverify") private Boolean noverify; + /** + * 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 + */ + @Parameter(property = "run.jvmArguments") + private String jvmArguments; + /** * Arguments that should be passed to the application. On command line use commas to * separate multiple arguments. @@ -151,6 +163,7 @@ public class RunMojo extends AbstractDependencyFilterMojo { private void run(String startClassName) throws MojoExecutionException { List args = new ArrayList(); addAgents(args); + addJvmArgs(args); addClasspath(args); args.add(startClassName); addArgs(args); @@ -163,10 +176,15 @@ public class RunMojo extends AbstractDependencyFilterMojo { } } + private void addJvmArgs(List args) { + String[] jvmArgs = parseArgs(this.jvmArguments); + Collections.addAll(args, jvmArgs); + logArguments("JVM argument(s): ", jvmArgs); + } + private void addArgs(List args) { - for (String arg : this.arguments) { - args.add(arg); - } + Collections.addAll(args, this.arguments); + logArguments("Application argument(s): ", this.arguments); } private void addClasspath(List args) throws MojoExecutionException { @@ -240,7 +258,7 @@ public class RunMojo extends AbstractDependencyFilterMojo { } } - private void addResources(List urls) throws MalformedURLException, IOException { + private void addResources(List urls) throws IOException { if (this.addResources) { for (Resource resource : this.project.getResources()) { File directory = new File(resource.getDirectory()); @@ -266,6 +284,36 @@ public class RunMojo extends AbstractDependencyFilterMojo { } } + private void logArguments(String message, String[] args) { + StringBuffer sb = new StringBuffer(message); + for (String arg : args) { + sb.append(arg).append(" "); + } + getLog().debug(sb.toString().trim()); + } + + /** + * Parse the arguments parameters and return individual arguments. + * + * @param arguments the arguments line to parse + * @return the individual arguments + */ + static String[] parseArgs(String arguments) { + if (arguments == null || arguments.trim().isEmpty()) { + return new String[]{}; + } + String args = arguments.replace('\n', ' '); + args = args.replace('\t', ' '); + + try { + return CommandLineUtils.translateCommandline(args); + } + catch (Exception e) { + throw new IllegalArgumentException("Failed to parse arguments [" + arguments + "]", e); + } + } + + private static class TestArtifactFilter extends AbstractArtifactFeatureFilter { public TestArtifactFilter() { super("", Artifact.SCOPE_TEST); diff --git a/spring-boot-tools/spring-boot-maven-plugin/src/site/apt/examples/run-debug.apt.vm b/spring-boot-tools/spring-boot-maven-plugin/src/site/apt/examples/run-debug.apt.vm new file mode 100644 index 00000000000..eef58d476c9 --- /dev/null +++ b/spring-boot-tools/spring-boot-maven-plugin/src/site/apt/examples/run-debug.apt.vm @@ -0,0 +1,54 @@ + ----- + Debug the application + ----- + Stephane Nicoll + ----- + 2014-05-14 + ----- + + The <<>> goal forks a process for the boot application. It is possible to specify jvm arguments + to that forked process. The following configuration suspend the process until a debugger has joined + on port 5005 + +--- + + ... + + ... + + ... + + ${project.groupId} + ${project.artifactId} + ${project.version} + + + + run + + + + -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005 + + + + + ... + + ... + + ... + + ... + +--- + + These arguments can be specified on the command line as well, make sure to wrap that properly, + that is: + +--- +mvn spring-boot:run -Drun.jvmArguments="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005" +--- + + + diff --git a/spring-boot-tools/spring-boot-maven-plugin/src/site/apt/index.apt b/spring-boot-tools/spring-boot-maven-plugin/src/site/apt/index.apt index b7f2e3f4128..515b38b41a0 100644 --- a/spring-boot-tools/spring-boot-maven-plugin/src/site/apt/index.apt +++ b/spring-boot-tools/spring-boot-maven-plugin/src/site/apt/index.apt @@ -40,6 +40,8 @@ Spring Boot Maven Plugin * {{{./examples/exclude-dependency.html}Exclude a dependency}} + * {{{./examples/run-debug.html}Debug the application}} + [] diff --git a/spring-boot-tools/spring-boot-maven-plugin/src/site/apt/usage.apt.vm b/spring-boot-tools/spring-boot-maven-plugin/src/site/apt/usage.apt.vm index 9afbd2544e2..c329aab97f2 100644 --- a/spring-boot-tools/spring-boot-maven-plugin/src/site/apt/usage.apt.vm +++ b/spring-boot-tools/spring-boot-maven-plugin/src/site/apt/usage.apt.vm @@ -99,6 +99,10 @@ Usage mvn spring-boot:run --- + The application is forked in a separate process. If you need to specify some JVM arguments + (i.e. for debugging purposes), you can use the <<>> parameter, see + {{{./examples/run-debug.html}Debug the application}} for more details. + By default, any <> folder will be added to the application classpath when you run the application. This allows hot refreshing of resources which can be very useful when developing web applications. For example, you can work on HTML, CSS or JavaScipt diff --git a/spring-boot-tools/spring-boot-maven-plugin/src/site/site.xml b/spring-boot-tools/spring-boot-maven-plugin/src/site/site.xml index 93cddec5b74..e72b40577cf 100644 --- a/spring-boot-tools/spring-boot-maven-plugin/src/site/site.xml +++ b/spring-boot-tools/spring-boot-maven-plugin/src/site/site.xml @@ -9,6 +9,7 @@ + diff --git a/spring-boot-tools/spring-boot-maven-plugin/src/test/java/org/springframework/boot/maven/RunMojoTests.java b/spring-boot-tools/spring-boot-maven-plugin/src/test/java/org/springframework/boot/maven/RunMojoTests.java new file mode 100644 index 00000000000..275170da702 --- /dev/null +++ b/spring-boot-tools/spring-boot-maven-plugin/src/test/java/org/springframework/boot/maven/RunMojoTests.java @@ -0,0 +1,75 @@ +/* + * Copyright 2012-2014 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 + * + * http://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 static org.junit.Assert.*; + +import org.junit.Test; + +/** + * + * @author Stephane Nicoll + */ +public class RunMojoTests { + + @Test + public void parseNull() { + String[] args = RunMojo.parseArgs(null); + assertNotNull(args); + assertEquals(0, args.length); + } + + @Test + public void parseEmpty() { + String[] args = RunMojo.parseArgs(" "); + assertNotNull(args); + assertEquals(0, args.length); + } + + @Test + public void parseDebugFlags() { + String[] args = RunMojo.parseArgs("-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005"); + assertEquals(2, args.length); + assertEquals("-Xdebug", args[0]); + assertEquals("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005", args[1]); + } + + @Test + public void parseWithExtraSpaces() { + String[] args = RunMojo.parseArgs(" -Dfoo=bar -Dfoo2=bar2 "); + assertEquals(2, args.length); + assertEquals("-Dfoo=bar", args[0]); + assertEquals("-Dfoo2=bar2", args[1]); + } + + @Test + public void parseWithNewLinesAndTabs() { + String[] args = RunMojo.parseArgs(" -Dfoo=bar \n" + + "\t\t -Dfoo2=bar2 "); + assertEquals(2, args.length); + assertEquals("-Dfoo=bar", args[0]); + assertEquals("-Dfoo2=bar2", args[1]); + } + + @Test + public void quoteHandledProperly() { + String[] args = RunMojo.parseArgs("-Dvalue=\"My Value\" "); + assertEquals(1, args.length); + assertEquals("-Dvalue=My Value", args[0]); + } + +}