Commit a9378420 authored by Stephane Nicoll's avatar Stephane Nicoll

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
parent 5249f54c
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.boot.maven.it</groupId>
<artifactId>run-jvmargs</artifactId>
<version>0.0.1.BUILD-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<plugins>
<plugin>
<groupId>@project.groupId@</groupId>
<artifactId>@project.artifactId@</artifactId>
<version>@project.version@</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<jvmArguments>-Dfoo="value 1" -Dbar=value2</jvmArguments>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
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");
}
}
def file = new File(basedir, "build.log")
return file.text.contains("I haz been run")
......@@ -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<String> args = new ArrayList<String>();
addAgents(args);
addJvmArgs(args);
addClasspath(args);
args.add(startClassName);
addArgs(args);
......@@ -163,10 +176,15 @@ public class RunMojo extends AbstractDependencyFilterMojo {
}
}
private void addJvmArgs(List<String> args) {
String[] jvmArgs = parseArgs(this.jvmArguments);
Collections.addAll(args, jvmArgs);
logArguments("JVM argument(s): ", jvmArgs);
}
private void addArgs(List<String> args) {
for (String arg : this.arguments) {
args.add(arg);
}
Collections.addAll(args, this.arguments);
logArguments("Application argument(s): ", this.arguments);
}
private void addClasspath(List<String> args) throws MojoExecutionException {
......@@ -240,7 +258,7 @@ public class RunMojo extends AbstractDependencyFilterMojo {
}
}
private void addResources(List<URL> urls) throws MalformedURLException, IOException {
private void addResources(List<URL> 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);
......
-----
Debug the application
-----
Stephane Nicoll
-----
2014-05-14
-----
The <<<run>>> 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>
...
<build>
...
<plugins>
...
<plugin>
<groupId>${project.groupId}</groupId>
<artifactId>${project.artifactId}</artifactId>
<version>${project.version}</version>
<executions>
<execution>
<goals>
<goal>run</goal>
</goals>
<configuration>
<jvmArguments>
-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005
</jvmArguments>
</configuration>
</execution>
</executions>
...
</plugin>
...
</plugins>
...
</build>
...
</project>
---
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"
---
......@@ -40,6 +40,8 @@ Spring Boot Maven Plugin
* {{{./examples/exclude-dependency.html}Exclude a dependency}}
* {{{./examples/run-debug.html}Debug the application}}
[]
......
......@@ -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 <<<jvmArguments>>> parameter, see
{{{./examples/run-debug.html}Debug the application}} for more details.
By default, any <<src/main/resources>> 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
......
......@@ -9,6 +9,7 @@
<menu name="Examples">
<item name="Custom repackage classifier" href="examples/repackage-classifier.html"/>
<item name="Exclude a dependency" href="examples/exclude-dependency.html"/>
<item name="Debug the application" href="examples/run-debug.html"/>
</menu>
<menu ref="reports"/>
</body>
......
/*
* 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]);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment