Polish "Add support for environment variables"

Closes gh-12800
pull/12955/merge
Stephane Nicoll 7 years ago
parent 95f7e3ca37
commit 40b7e02793

@ -2771,14 +2771,6 @@ See {spring-boot-maven-plugin-site}/examples/run-debug.html[this example] for mo
details. details.
[[howto-set-env-maven-run]]
=== Run Spring Boot Application with Environment variables Started with Maven
To set up the environment variables to a Spring Boot application that was started with Maven, you
can use the `environmentVariables` property of the {spring-boot-maven-plugin-site}[maven plugin].
See {spring-boot-maven-plugin-site}/examples/run-with-env.html[this example] for more
details.
[[howto-build-an-executable-archive-with-ant]] [[howto-build-an-executable-archive-with-ant]]
=== Build an Executable Archive from Ant without Using `spring-boot-antlib` === Build an Executable Archive from Ant without Using `spring-boot-antlib`

@ -69,12 +69,8 @@ public class RunProcess {
return run(waitForProcess, Arrays.asList(args), Collections.emptyMap()); return run(waitForProcess, Arrays.asList(args), Collections.emptyMap());
} }
public int run(boolean waitForProcess, String[] args, Map<String, String> environmentVariables) throws IOException { public int run(boolean waitForProcess, Collection<String> args,
return run(waitForProcess, Arrays.asList(args), environmentVariables); Map<String, String> environmentVariables) throws IOException {
}
protected int run(boolean waitForProcess, Collection<String> args, Map<String, String> environmentVariables)
throws IOException {
ProcessBuilder builder = new ProcessBuilder(this.command); ProcessBuilder builder = new ProcessBuilder(this.command);
builder.directory(this.workingDirectory); builder.directory(this.workingDirectory);
builder.command().addAll(args); builder.command().addAll(args);

@ -18,7 +18,6 @@ package org.test;
public class SampleApplication { public class SampleApplication {
public static void main(String[] args) { public static void main(String[] args) {
assertEnvValue("ENV1", "5000"); assertEnvValue("ENV1", "5000");
assertEnvValue("ENV2", "Some Text"); assertEnvValue("ENV2", "Some Text");
@ -26,14 +25,13 @@ public class SampleApplication {
assertEnvValue("ENV4", ""); assertEnvValue("ENV4", "");
System.out.println("I haz been run"); System.out.println("I haz been run");
} }
private static void assertEnvValue(String envKey, String expectedValue) {
static void assertEnvValue(String envKey, String expectedValue) {
String actual = System.getenv(envKey); String actual = System.getenv(envKey);
if (!expectedValue.equals(actual)) { if (!expectedValue.equals(actual)) {
throw new IllegalStateException("env property [" + envKey + "] mismatch (got [" + actual + "], expected [" + expectedValue + "]"); throw new IllegalStateException("env property [" + envKey + "] mismatch "
+ "(got [" + actual + "], expected [" + expectedValue + "]");
} }
} }

@ -24,7 +24,6 @@ import java.net.URL;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -118,12 +117,12 @@ public abstract class AbstractRunMojo extends AbstractDependencyFilterMojo {
private Map<String, String> systemPropertyVariables; private Map<String, String> systemPropertyVariables;
/** /**
* List of Environment variables that should be associated with the forked process used to run the * List of Environment variables that should be associated with the forked process
* application. * used to run the application. NOTE: the use of Environment variables means that
* <p>NOTE: the use of Environment variables means that processes will be started by forking a * processes will be started by forking a new JVM.
* new JVM. * @since 2.1
*/ */
@Parameter(property = "spring-boot.run.environmentVariables") @Parameter
private Map<String, String> environmentVariables; private Map<String, String> environmentVariables;
/** /**
@ -231,7 +230,6 @@ public abstract class AbstractRunMojo extends AbstractDependencyFilterMojo {
return (this.environmentVariables != null && !this.environmentVariables.isEmpty()); return (this.environmentVariables != null && !this.environmentVariables.isEmpty());
} }
private boolean hasWorkingDirectorySet() { private boolean hasWorkingDirectorySet() {
return this.workingDirectory != null; return this.workingDirectory != null;
} }
@ -273,22 +271,19 @@ public abstract class AbstractRunMojo extends AbstractDependencyFilterMojo {
private void doRunWithForkedJvm(String startClassName) private void doRunWithForkedJvm(String startClassName)
throws MojoExecutionException, MojoFailureException { throws MojoExecutionException, MojoFailureException {
List<String> args = new ArrayList<>(); List<String> args = new ArrayList<>();
Map<String, String> envVariables = new LinkedHashMap<>();
addAgents(args); addAgents(args);
addJvmArgs(args); addJvmArgs(args);
addClasspath(args); addClasspath(args);
args.add(startClassName); args.add(startClassName);
addArgs(args); addArgs(args);
addEnvironmentVariables(envVariables); runWithForkedJvm(this.workingDirectory, args, determineEnvironmentVariables());
runWithForkedJvm(this.workingDirectory, args, envVariables);
} }
/** /**
* Run with a forked VM, using the specified command line arguments. * Run with a forked VM, using the specified command line arguments.
* @param workingDirectory the working directory of the forked JVM * @param workingDirectory the working directory of the forked JVM
* @param args the arguments (JVM arguments and application arguments) * @param args the arguments (JVM arguments and application arguments)
* @param environmentVariables the environment variables; * @param environmentVariables the environment variables
* @throws MojoExecutionException in case of MOJO execution errors * @throws MojoExecutionException in case of MOJO execution errors
* @throws MojoFailureException in case of MOJO failures * @throws MojoFailureException in case of MOJO failures
*/ */
@ -316,27 +311,24 @@ public abstract class AbstractRunMojo extends AbstractDependencyFilterMojo {
return runArguments; return runArguments;
} }
/** /**
* Resolve the environment variables to use. * Resolve the environment variables to use.
*
* @return a {@link EnvVariables} defining the environment variables * @return a {@link EnvVariables} defining the environment variables
*/ */
protected EnvVariables resolveEnvVariables() { protected EnvVariables resolveEnvVariables() {
return new EnvVariables(this.environmentVariables); return new EnvVariables(this.environmentVariables);
} }
private void addArgs(List<String> args) { private void addArgs(List<String> args) {
RunArguments applicationArguments = resolveApplicationArguments(); RunArguments applicationArguments = resolveApplicationArguments();
Collections.addAll(args, applicationArguments.asArray()); Collections.addAll(args, applicationArguments.asArray());
logArguments("Application argument(s): ", this.arguments); logArguments("Application argument(s): ", this.arguments);
} }
private void addEnvironmentVariables(Map<String, String> environmentVariables) { private Map<String, String> determineEnvironmentVariables() {
EnvVariables envVariables = resolveEnvVariables(); EnvVariables envVariables = resolveEnvVariables();
environmentVariables.putAll(envVariables.asMap());
logArguments("Environment variable(s): ", envVariables.asArray()); logArguments("Environment variable(s): ", envVariables.asArray());
return envVariables.asMap();
} }
/** /**

@ -29,53 +29,39 @@ import java.util.Map;
*/ */
class EnvVariables { class EnvVariables {
private static final String SPACE = "="; private final Map<String, String> variables;
private static final String NO_VALUE = "";
private final Map<String, String> args = new LinkedHashMap<>(); EnvVariables(Map<String, String> variables) {
this.variables = parseEnvVariables(variables);
EnvVariables(Map<String, String> args) {
this.args.putAll(getArgs(args));
}
Map<String, String> asMap() {
return Collections.unmodifiableMap(this.args);
}
String[] asArray() {
List<String> args = new ArrayList<>(this.args.size());
for (Map.Entry<String, String> arg : this.args.entrySet()) {
args.add(arg.getKey() + SPACE + arg.getValue());
}
return args.toArray(new String[args.size()]);
} }
private static Map<String, String> parseEnvVariables(Map<String, String> args) {
private Map<String, String> getArgs(Map<String, String> args) {
if (args == null || args.isEmpty()) { if (args == null || args.isEmpty()) {
return Collections.emptyMap(); return Collections.emptyMap();
} }
Map<String, String> result = new LinkedHashMap<>(); Map<String, String> result = new LinkedHashMap<>();
for (Map.Entry<String, String> e : args.entrySet()) { for (Map.Entry<String, String> e : args.entrySet()) {
if (hasText(e.getKey())) { if (e.getKey() != null) {
result.put(e.getKey(), getValue(e.getValue())); result.put(e.getKey(), getValue(e.getValue()));
} }
} }
return result; return result;
} }
private String getValue(String value) { private static String getValue(String value) {
if (hasText(value)) { return (value != null ? value : "");
return value;
}
return NO_VALUE;
} }
private boolean hasText(String source) { public Map<String, String> asMap() {
return source != null && !source.trim().isEmpty(); return Collections.unmodifiableMap(this.variables);
} }
public String[] asArray() {
List<String> args = new ArrayList<>(this.variables.size());
for (Map.Entry<String, String> arg : this.variables.entrySet()) {
args.add(arg.getKey() + "=" + arg.getValue());
}
return args.toArray(new String[0]);
}
} }

@ -66,14 +66,15 @@ public class RunMojo extends AbstractRunMojo {
} }
@Override @Override
protected void runWithForkedJvm(File workingDirectory, List<String> args, Map<String, String> environmentVariables) protected void runWithForkedJvm(File workingDirectory, List<String> args,
Map<String, String> environmentVariables)
throws MojoExecutionException { throws MojoExecutionException {
try { try {
RunProcess runProcess = new RunProcess(workingDirectory, RunProcess runProcess = new RunProcess(workingDirectory,
new JavaExecutable().toString()); new JavaExecutable().toString());
Runtime.getRuntime() Runtime.getRuntime()
.addShutdownHook(new Thread(new RunProcessKiller(runProcess))); .addShutdownHook(new Thread(new RunProcessKiller(runProcess)));
int exitCode = runProcess.run(true, args.toArray(new String[0]), environmentVariables); int exitCode = runProcess.run(true, args, environmentVariables);
if (exitCode == 0 || exitCode == EXIT_CODE_SIGINT) { if (exitCode == 0 || exitCode == EXIT_CODE_SIGINT) {
return; return;
} }

@ -47,7 +47,6 @@ import org.springframework.boot.loader.tools.RunProcess;
* stopped after. * stopped after.
* *
* @author Stephane Nicoll * @author Stephane Nicoll
* @author Dmytro Nosan
* @since 1.3.0 * @since 1.3.0
* @see StopMojo * @see StopMojo
*/ */
@ -90,7 +89,8 @@ public class StartMojo extends AbstractRunMojo {
private final Object lock = new Object(); private final Object lock = new Object();
@Override @Override
protected void runWithForkedJvm(File workingDirectory, List<String> args, Map<String, String> environmentVariables) protected void runWithForkedJvm(File workingDirectory, List<String> args,
Map<String, String> environmentVariables)
throws MojoExecutionException, MojoFailureException { throws MojoExecutionException, MojoFailureException {
RunProcess runProcess = runProcess(workingDirectory, args, environmentVariables); RunProcess runProcess = runProcess(workingDirectory, args, environmentVariables);
try { try {
@ -102,12 +102,13 @@ public class StartMojo extends AbstractRunMojo {
} }
} }
private RunProcess runProcess(File workingDirectory, List<String> args, Map<String, String> environmentVariables) private RunProcess runProcess(File workingDirectory, List<String> args,
Map<String, String> environmentVariables)
throws MojoExecutionException { throws MojoExecutionException {
try { try {
RunProcess runProcess = new RunProcess(workingDirectory, RunProcess runProcess = new RunProcess(workingDirectory,
new JavaExecutable().toString()); new JavaExecutable().toString());
runProcess.run(false, args.toArray(new String[0]), environmentVariables); runProcess.run(false, args, environmentVariables);
return runProcess; return runProcess;
} }
catch (Exception ex) { catch (Exception ex) {

@ -0,0 +1,50 @@
-----
Using environment variables
-----
Dmytro Nosan
-----
2018-04-08
-----
Environment variables can be specified using the <<<environmentVariables>>> attribute.
The following sets the 'ENV1', 'ENV2', 'ENV3', 'ENV4' env variables:
---
<project>
...
<build>
...
<plugins>
...
<plugin>
<groupId>${project.groupId}</groupId>
<artifactId>${project.artifactId}</artifactId>
<version>${project.version}</version>
<configuration>
<environmentVariables>
<ENV1>5000</ENV1>
<ENV2>Some Text</ENV2>
<ENV3/>
<ENV4></ENV4>
</environmentVariables>
</configuration>
...
</plugin>
...
</plugins>
...
</build>
...
</project>
---
If the value is empty or not defined (i.e. <<<<MY_ENV/>>>>), the env variable is set
with an empty String as the value.
Any String typed Maven variable can be passed as system properties. Any attempt to pass
any other Maven variable type (e.g. a <<<List>>> or a <<<URL>>> variable) will cause the
variable expression to be passed literally (unevaluated).
Environment variables defined this way take precedence over existing values.

@ -1,43 +0,0 @@
-----
Specify environment variables
-----
Dmytro Nosan
-----
2018-04-08
-----
The environmnet variables to use for a particular application can be specified using the <<<environmentVariables>>>
argument. The following configuration enables the 'ENV1', 'ENV2', 'ENV3', 'ENV4' env variables:
---
<project>
...
<build>
...
<plugins>
...
<plugin>
<groupId>${project.groupId}</groupId>
<artifactId>${project.artifactId}</artifactId>
<version>${project.version}</version>
<configuration>
<environmentVariables>
<ENV1>5000</ENV1>
<ENV2>Some Text</ENV2>
<ENV3/>
<ENV4></ENV4>
</environmentVariables>
</configuration>
...
</plugin>
...
</plugins>
...
</build>
...
</project>
---
Note that since you specified some Environment variables, the process is forked automatically.

@ -54,14 +54,14 @@ Spring Boot Maven Plugin
* {{{./examples/run-system-properties.html}Using system properties}} * {{{./examples/run-system-properties.html}Using system properties}}
* {{{./examples/run-env-variables.html}Using environment variables}}
* {{{./examples/it-random-port.html}Random port for integration tests}} * {{{./examples/it-random-port.html}Random port for integration tests}}
* {{{./examples/it-skip.html}Skip integration tests}} * {{{./examples/it-skip.html}Skip integration tests}}
* {{{./examples/run-profiles.html}Specify active profiles}} * {{{./examples/run-profiles.html}Specify active profiles}}
* {{{./examples/run-with-env.html}Specify Environment variables}}
* {{{./examples/build-info.html}Generate build information}} * {{{./examples/build-info.html}Generate build information}}
* {{{./examples/custom-layout.html}Custom layout}} * {{{./examples/custom-layout.html}Custom layout}}

@ -140,11 +140,12 @@ mvn spring-boot:run
If you need to specify some JVM arguments (i.e. for debugging purposes), you can use 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}} the <<<jvmArguments>>> parameter, see {{{./examples/run-debug.html}Debug the application}}
for more details. There is also explicit support for more details. There is also explicit support for
{{{./examples/run-system-properties.html}for system properties}} and {{{./examples/run-system-properties.html}system properties}} and
{{{./examples/run-with-env.html}environment variables}}. As a convenience, the profiles {{{./examples/run-env-variables.html}environment variables}}.
to enable are handled by a specific property (<<<profiles>>>), see
{{{./examples/run-profiles.html}Specify active profiles}}. As a convenience, the profiles to enable are handled by a specific property (
<<<profiles>>>), see {{{./examples/run-profiles.html}Specify active profiles}}.
Spring Boot 1.3 has introduced <<<devtools>>>, a module to improve the development-time Spring Boot 1.3 has introduced <<<devtools>>>, a module to improve the development-time
experience when working on Spring Boot applications. To enable it, just add the following experience when working on Spring Boot applications. To enable it, just add the following

@ -12,10 +12,10 @@
<item name="Exclude a dependency" href="examples/exclude-dependency.html"/> <item name="Exclude a dependency" href="examples/exclude-dependency.html"/>
<item name="Debug the application" href="examples/run-debug.html"/> <item name="Debug the application" href="examples/run-debug.html"/>
<item name="Using system properties" href="examples/run-system-properties.html"/> <item name="Using system properties" href="examples/run-system-properties.html"/>
<item name="Using environment variable" href="examples/run-env-variables.html"/>
<item name="Random port for integration tests" href="examples/it-random-port.html"/> <item name="Random port for integration tests" href="examples/it-random-port.html"/>
<item name="Skip integration tests" href="examples/it-skip.html"/> <item name="Skip integration tests" href="examples/it-skip.html"/>
<item name="Specify active profiles" href="examples/run-profiles.html"/> <item name="Specify active profiles" href="examples/run-profiles.html"/>
<item name="Specify environment variables" href="examples/run-with-env.html"/>
<item name="Generate build information" href="examples/build-info.html"/> <item name="Generate build information" href="examples/build-info.html"/>
<item name="Custom layout" href="examples/custom-layout.html"/> <item name="Custom layout" href="examples/custom-layout.html"/>
</menu> </menu>

@ -22,6 +22,7 @@ import java.util.Map;
import org.junit.Test; import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.entry;
/** /**
* Tests for {@link EnvVariables}. * Tests for {@link EnvVariables}.
@ -33,40 +34,29 @@ public class EnvVariablesTests {
@Test @Test
public void asNull() { public void asNull() {
Map<String, String> args = new EnvVariables(null).asMap(); Map<String, String> args = new EnvVariables(null).asMap();
assertThat(args).hasSize(0); assertThat(args).isEmpty();
} }
@Test @Test
public void asArray() { public void asArray() {
assertThat(new EnvVariables(getTestArgs()).asArray()) assertThat(new EnvVariables(getTestArgs()).asArray())
.contains("key=My Value") .contains("key=My Value", "key1= tt ", "key2= ", "key3=");
.contains("key1= tt ")
.contains("key2=")
.contains("key3=");
} }
@Test @Test
public void asMap() { public void asMap() {
assertThat(new EnvVariables(getTestArgs()).asMap()) assertThat(new EnvVariables(getTestArgs()).asMap()).containsExactly(
.containsEntry("key", "My Value") entry("key", "My Value"), entry("key1", " tt "), entry("key2", " "),
.containsEntry("key1", " tt ") entry("key3", ""));
.containsEntry("key2", "")
.containsEntry("key3", "");
} }
private Map<String, String> getTestArgs() { private Map<String, String> getTestArgs() {
Map<String, String> args = new LinkedHashMap<>(); Map<String, String> args = new LinkedHashMap<>();
args.put("key", "My Value"); args.put("key", "My Value");
//should not be trimmed
args.put("key1", " tt "); args.put("key1", " tt ");
args.put("key2", " "); args.put("key2", " ");
args.put("key3", null); args.put("key3", null);
return args; return args;
} }
} }

Loading…
Cancel
Save