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 0000000000..cff254f939 --- /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 0000000000..437557e0f8 --- /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 0000000000..841c4a97de --- /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 141ebf714c..fdfb170819 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 0000000000..eef58d476c --- /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 b7f2e3f412..515b38b41a 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 9afbd2544e..c329aab97f 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 93cddec5b7..e72b40577c 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 0000000000..275170da70 --- /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]); + } + +}