Support inlining a conf script into the default launch script

See gh-9590
pull/10235/merge
JustinKSU 8 years ago committed by Andy Wilkinson
parent c79b76847b
commit 5ee28a08e1

@ -760,6 +760,11 @@ for Gradle and to `${project.name}` for Maven.
|`confFolder` |`confFolder`
|The default value for `CONF_FOLDER`. Defaults to the folder containing the jar. |The default value for `CONF_FOLDER`. Defaults to the folder containing the jar.
|`inlinedConfScript`
|Reference to a file script that should be inlined in the default launch script.
This can be used to set environmental variables such as `JAVA_OPTS` before
any external config files are loaded.
|`logFolder` |`logFolder`
|The default value for `LOG_FOLDER`. Only valid for an `init.d` service. |The default value for `LOG_FOLDER`. Only valid for an `init.d` service.

@ -16,34 +16,35 @@
package org.springframework.boot.loader.tools; package org.springframework.boot.loader.tools;
import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.springframework.util.FileCopyUtils;
/** /**
* Default implementation of {@link LaunchScript}. Provides the default Spring Boot launch * Default implementation of {@link LaunchScript}. Provides the default Spring Boot launch
* script or can load a specific script File. Also support mustache style template * script or can load a specific script File. Also support mustache style template
* expansion of the form <code>{{name:default}}</code>. * expansion of the form <code>{{name:default}}</code>.
* *
* @author Phillip Webb * @author Phillip Webb
* @author Justin Rosenberg
* @since 1.3.0 * @since 1.3.0
*/ */
public class DefaultLaunchScript implements LaunchScript { public class DefaultLaunchScript implements LaunchScript {
private static final Charset UTF_8 = Charset.forName("UTF-8"); private static final Charset UTF_8 = Charset.forName("UTF-8");
private static final int BUFFER_SIZE = 4096;
private static final Pattern PLACEHOLDER_PATTERN = Pattern private static final Pattern PLACEHOLDER_PATTERN = Pattern
.compile("\\{\\{(\\w+)(:.*?)?\\}\\}(?!\\})"); .compile("\\{\\{(\\w+)(:.*?)?\\}\\}(?!\\})");
private static final List<String> FILE_PATH_KEYS = Arrays.asList("inlinedConfScript");
private final String content; private final String content;
/** /**
@ -57,45 +58,52 @@ public class DefaultLaunchScript implements LaunchScript {
this.content = expandPlaceholders(content, properties); this.content = expandPlaceholders(content, properties);
} }
/**
* Loads file contents.
* @param file File to load. If null, will load default launch.script
* @return String representation of file contents.
* @throws IOException if file is not found our can't be loaded.
*/
private String loadContent(File file) throws IOException { private String loadContent(File file) throws IOException {
final byte[] fileBytes;
if (file == null) { if (file == null) {
return loadContent(getClass().getResourceAsStream("launch.script")); fileBytes = FileCopyUtils
.copyToByteArray(getClass().getResourceAsStream("launch.script"));
} }
return loadContent(new FileInputStream(file)); else {
} fileBytes = FileCopyUtils.copyToByteArray(file);
private String loadContent(InputStream inputStream) throws IOException {
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
copy(inputStream, outputStream);
return new String(outputStream.toByteArray(), UTF_8);
}
finally {
inputStream.close();
} }
return new String(fileBytes, UTF_8);
} }
private void copy(InputStream inputStream, OutputStream outputStream) /**
* Replaces variable placeholders in file with specified property values.
* @param content String with variables defined in {{variable:default}} format.
* @param properties Key value pairs for variables to replace
* @return Updated String
* @throws IOException if a file property value or path is specified and the file
* cannot be loaded.
*/
private String expandPlaceholders(String content, Map<?, ?> properties)
throws IOException { throws IOException {
byte[] buffer = new byte[BUFFER_SIZE];
int bytesRead = -1;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
outputStream.flush();
}
private String expandPlaceholders(String content, Map<?, ?> properties) {
StringBuffer expanded = new StringBuffer(); StringBuffer expanded = new StringBuffer();
Matcher matcher = PLACEHOLDER_PATTERN.matcher(content); Matcher matcher = PLACEHOLDER_PATTERN.matcher(content);
while (matcher.find()) { while (matcher.find()) {
String name = matcher.group(1); String name = matcher.group(1);
String value = matcher.group(2); final String value;
String defaultValue = matcher.group(2);
if (properties != null && properties.containsKey(name)) { if (properties != null && properties.containsKey(name)) {
value = (String) properties.get(name); Object propertyValue = properties.get(name);
if (FILE_PATH_KEYS.contains(name)) {
value = parseFilePropertyValue(properties.get(name));
}
else {
value = propertyValue.toString();
}
} }
else { else {
value = (value == null ? matcher.group(0) : value.substring(1)); value = (defaultValue == null ? matcher.group(0)
: defaultValue.substring(1));
} }
matcher.appendReplacement(expanded, value.replace("$", "\\$")); matcher.appendReplacement(expanded, value.replace("$", "\\$"));
} }
@ -103,6 +111,26 @@ public class DefaultLaunchScript implements LaunchScript {
return expanded.toString(); return expanded.toString();
} }
/**
* Loads file based on File object or String path.
* @param propertyValue File Object or String path to file.
* @return File contents.
* @throws IOException if a file property value or path is specified and the file
* cannot be loaded.
*/
private String parseFilePropertyValue(Object propertyValue) throws IOException {
if (propertyValue instanceof File) {
return loadContent((File) propertyValue);
}
else {
return loadContent(new File(propertyValue.toString()));
}
}
/**
* The content of the launch script as a byte array.
* @return Byte representation of script.
*/
@Override @Override
public byte[] toByteArray() { public byte[] toByteArray() {
return this.content.getBytes(UTF_8); return this.content.getBytes(UTF_8);

@ -46,6 +46,9 @@ done
jarfolder="$( (cd "$(dirname "$jarfile")" && pwd -P) )" jarfolder="$( (cd "$(dirname "$jarfile")" && pwd -P) )"
cd "$WORKING_DIR" || exit 1 cd "$WORKING_DIR" || exit 1
# Inline script specified in build properties
{{inlinedConfScript:}}
# Source any config file # Source any config file
configfile="$(basename "${jarfile%.*}.conf")" configfile="$(basename "${jarfile%.*}.conf")"

@ -17,6 +17,7 @@
package org.springframework.boot.loader.tools; package org.springframework.boot.loader.tools;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -126,6 +127,14 @@ public class DefaultLaunchScriptTests {
assertThatPlaceholderCanBeReplaced("stopWaitTime"); assertThatPlaceholderCanBeReplaced("stopWaitTime");
} }
@Test
public void inlinedConfScriptFileLoad() throws IOException {
DefaultLaunchScript script = new DefaultLaunchScript(null,
createProperties("inlinedConfScript:src/test/resources/example.script"));
String content = new String(script.toByteArray());
assertThat(content).contains("FOO=BAR");
}
@Test @Test
public void defaultForUseStartStopDaemonIsTrue() throws Exception { public void defaultForUseStartStopDaemonIsTrue() throws Exception {
DefaultLaunchScript script = new DefaultLaunchScript(null, null); DefaultLaunchScript script = new DefaultLaunchScript(null, null);
@ -185,6 +194,15 @@ public class DefaultLaunchScriptTests {
assertThat(content).isEqualTo("hello"); assertThat(content).isEqualTo("hello");
} }
@Test
public void expandVariablesCanDefaultToBlank() throws Exception {
File file = this.temporaryFolder.newFile();
FileCopyUtils.copy("s{{p:}}{{r:}}ing".getBytes(), file);
DefaultLaunchScript script = new DefaultLaunchScript(file, null);
String content = new String(script.toByteArray());
assertThat(content).isEqualTo("sing");
}
@Test @Test
public void expandVariablesWithDefaultsOverride() throws Exception { public void expandVariablesWithDefaultsOverride() throws Exception {
File file = this.temporaryFolder.newFile(); File file = this.temporaryFolder.newFile();

Loading…
Cancel
Save