Skip to content

Commit 5ee28a0

Browse files
JustinKSUwilkinsona
authored andcommitted
Support inlining a conf script into the default launch script
See gh-9590
1 parent c79b768 commit 5ee28a0

File tree

5 files changed

+86
-31
lines changed

5 files changed

+86
-31
lines changed

spring-boot-docs/src/main/asciidoc/deployment.adoc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -760,6 +760,11 @@ for Gradle and to `${project.name}` for Maven.
760760
|`confFolder`
761761
|The default value for `CONF_FOLDER`. Defaults to the folder containing the jar.
762762

763+
|`inlinedConfScript`
764+
|Reference to a file script that should be inlined in the default launch script.
765+
This can be used to set environmental variables such as `JAVA_OPTS` before
766+
any external config files are loaded.
767+
763768
|`logFolder`
764769
|The default value for `LOG_FOLDER`. Only valid for an `init.d` service.
765770

spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/DefaultLaunchScript.java

Lines changed: 59 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -16,34 +16,35 @@
1616

1717
package org.springframework.boot.loader.tools;
1818

19-
import java.io.ByteArrayOutputStream;
2019
import java.io.File;
21-
import java.io.FileInputStream;
2220
import java.io.IOException;
23-
import java.io.InputStream;
24-
import java.io.OutputStream;
2521
import java.nio.charset.Charset;
22+
import java.util.Arrays;
23+
import java.util.List;
2624
import java.util.Map;
2725
import java.util.regex.Matcher;
2826
import java.util.regex.Pattern;
2927

28+
import org.springframework.util.FileCopyUtils;
29+
3030
/**
3131
* Default implementation of {@link LaunchScript}. Provides the default Spring Boot launch
3232
* script or can load a specific script File. Also support mustache style template
3333
* expansion of the form <code>{{name:default}}</code>.
3434
*
3535
* @author Phillip Webb
36+
* @author Justin Rosenberg
3637
* @since 1.3.0
3738
*/
3839
public class DefaultLaunchScript implements LaunchScript {
3940

4041
private static final Charset UTF_8 = Charset.forName("UTF-8");
4142

42-
private static final int BUFFER_SIZE = 4096;
43-
4443
private static final Pattern PLACEHOLDER_PATTERN = Pattern
4544
.compile("\\{\\{(\\w+)(:.*?)?\\}\\}(?!\\})");
4645

46+
private static final List<String> FILE_PATH_KEYS = Arrays.asList("inlinedConfScript");
47+
4748
private final String content;
4849

4950
/**
@@ -57,52 +58,79 @@ public DefaultLaunchScript(File file, Map<?, ?> properties) throws IOException {
5758
this.content = expandPlaceholders(content, properties);
5859
}
5960

61+
/**
62+
* Loads file contents.
63+
* @param file File to load. If null, will load default launch.script
64+
* @return String representation of file contents.
65+
* @throws IOException if file is not found our can't be loaded.
66+
*/
6067
private String loadContent(File file) throws IOException {
68+
final byte[] fileBytes;
6169
if (file == null) {
62-
return loadContent(getClass().getResourceAsStream("launch.script"));
70+
fileBytes = FileCopyUtils
71+
.copyToByteArray(getClass().getResourceAsStream("launch.script"));
6372
}
64-
return loadContent(new FileInputStream(file));
65-
}
66-
67-
private String loadContent(InputStream inputStream) throws IOException {
68-
try {
69-
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
70-
copy(inputStream, outputStream);
71-
return new String(outputStream.toByteArray(), UTF_8);
72-
}
73-
finally {
74-
inputStream.close();
73+
else {
74+
fileBytes = FileCopyUtils.copyToByteArray(file);
7575
}
76+
return new String(fileBytes, UTF_8);
7677
}
7778

78-
private void copy(InputStream inputStream, OutputStream outputStream)
79+
/**
80+
* Replaces variable placeholders in file with specified property values.
81+
* @param content String with variables defined in {{variable:default}} format.
82+
* @param properties Key value pairs for variables to replace
83+
* @return Updated String
84+
* @throws IOException if a file property value or path is specified and the file
85+
* cannot be loaded.
86+
*/
87+
private String expandPlaceholders(String content, Map<?, ?> properties)
7988
throws IOException {
80-
byte[] buffer = new byte[BUFFER_SIZE];
81-
int bytesRead = -1;
82-
while ((bytesRead = inputStream.read(buffer)) != -1) {
83-
outputStream.write(buffer, 0, bytesRead);
84-
}
85-
outputStream.flush();
86-
}
87-
88-
private String expandPlaceholders(String content, Map<?, ?> properties) {
8989
StringBuffer expanded = new StringBuffer();
9090
Matcher matcher = PLACEHOLDER_PATTERN.matcher(content);
9191
while (matcher.find()) {
9292
String name = matcher.group(1);
93-
String value = matcher.group(2);
93+
final String value;
94+
String defaultValue = matcher.group(2);
9495
if (properties != null && properties.containsKey(name)) {
95-
value = (String) properties.get(name);
96+
Object propertyValue = properties.get(name);
97+
if (FILE_PATH_KEYS.contains(name)) {
98+
value = parseFilePropertyValue(properties.get(name));
99+
}
100+
else {
101+
value = propertyValue.toString();
102+
}
96103
}
97104
else {
98-
value = (value == null ? matcher.group(0) : value.substring(1));
105+
value = (defaultValue == null ? matcher.group(0)
106+
: defaultValue.substring(1));
99107
}
100108
matcher.appendReplacement(expanded, value.replace("$", "\\$"));
101109
}
102110
matcher.appendTail(expanded);
103111
return expanded.toString();
104112
}
105113

114+
/**
115+
* Loads file based on File object or String path.
116+
* @param propertyValue File Object or String path to file.
117+
* @return File contents.
118+
* @throws IOException if a file property value or path is specified and the file
119+
* cannot be loaded.
120+
*/
121+
private String parseFilePropertyValue(Object propertyValue) throws IOException {
122+
if (propertyValue instanceof File) {
123+
return loadContent((File) propertyValue);
124+
}
125+
else {
126+
return loadContent(new File(propertyValue.toString()));
127+
}
128+
}
129+
130+
/**
131+
* The content of the launch script as a byte array.
132+
* @return Byte representation of script.
133+
*/
106134
@Override
107135
public byte[] toByteArray() {
108136
return this.content.getBytes(UTF_8);

spring-boot-tools/spring-boot-loader-tools/src/main/resources/org/springframework/boot/loader/tools/launch.script

100755100644
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ done
4646
jarfolder="$( (cd "$(dirname "$jarfile")" && pwd -P) )"
4747
cd "$WORKING_DIR" || exit 1
4848

49+
# Inline script specified in build properties
50+
{{inlinedConfScript:}}
51+
4952
# Source any config file
5053
configfile="$(basename "${jarfile%.*}.conf")"
5154

spring-boot-tools/spring-boot-loader-tools/src/test/java/org/springframework/boot/loader/tools/DefaultLaunchScriptTests.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.springframework.boot.loader.tools;
1818

1919
import java.io.File;
20+
import java.io.IOException;
2021
import java.util.HashMap;
2122
import java.util.Map;
2223

@@ -126,6 +127,14 @@ public void stopWaitTimeCanBeReplaced() throws Exception {
126127
assertThatPlaceholderCanBeReplaced("stopWaitTime");
127128
}
128129

130+
@Test
131+
public void inlinedConfScriptFileLoad() throws IOException {
132+
DefaultLaunchScript script = new DefaultLaunchScript(null,
133+
createProperties("inlinedConfScript:src/test/resources/example.script"));
134+
String content = new String(script.toByteArray());
135+
assertThat(content).contains("FOO=BAR");
136+
}
137+
129138
@Test
130139
public void defaultForUseStartStopDaemonIsTrue() throws Exception {
131140
DefaultLaunchScript script = new DefaultLaunchScript(null, null);
@@ -185,6 +194,15 @@ public void expandVariablesWithDefaults() throws Exception {
185194
assertThat(content).isEqualTo("hello");
186195
}
187196

197+
@Test
198+
public void expandVariablesCanDefaultToBlank() throws Exception {
199+
File file = this.temporaryFolder.newFile();
200+
FileCopyUtils.copy("s{{p:}}{{r:}}ing".getBytes(), file);
201+
DefaultLaunchScript script = new DefaultLaunchScript(file, null);
202+
String content = new String(script.toByteArray());
203+
assertThat(content).isEqualTo("sing");
204+
}
205+
188206
@Test
189207
public void expandVariablesWithDefaultsOverride() throws Exception {
190208
File file = this.temporaryFolder.newFile();
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
FOO=BAR

0 commit comments

Comments
 (0)