Support embedded jar initialization scripts
Update the Maven and Gradle plugin to generate fully executable jar files on Unix like machines. A launcher bash script is added to the front of the jar file which handles execution. The default execution script will either launch the application or handle init.d service operations (start/stop/restart) depending on if the application is executed directly, or via a symlink to init.d. See gh-1117
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright 2012-2015 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.loader.tools;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link LaunchScript}. Provides the default Spring Boot launch
|
||||
* script or can load a specific script File. Also support mustache style template
|
||||
* expansion of the form <code>{{name:default}}</code>.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public class DefaultLaunchScript implements LaunchScript {
|
||||
|
||||
private static final Charset UTF_8 = Charset.forName("UTF-8");
|
||||
|
||||
private static final int BUFFER_SIZE = 4096;
|
||||
|
||||
private static final Pattern PLACEHOLDER_PATTERN = Pattern
|
||||
.compile("\\{\\{(\\w+)(:.*?)?\\}\\}");
|
||||
|
||||
private final String content;
|
||||
|
||||
/**
|
||||
* Create a new {@link DefaultLaunchScript} instance.
|
||||
* @param file the source script file or {@code null} to use the default
|
||||
* @param properties an optional set of script properties used for variable expansion
|
||||
* @throws IOException if the script cannot be loaded
|
||||
*/
|
||||
public DefaultLaunchScript(File file, Map<?, ?> properties) throws IOException {
|
||||
String content = loadContent(file);
|
||||
this.content = expandPlaceholders(content, properties);
|
||||
}
|
||||
|
||||
private String loadContent(File file) throws IOException {
|
||||
if (file == null) {
|
||||
return loadContent(getClass().getResourceAsStream("launch.script"));
|
||||
}
|
||||
return loadContent(new FileInputStream(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();
|
||||
}
|
||||
}
|
||||
|
||||
private void copy(InputStream inputStream, OutputStream outputStream)
|
||||
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();
|
||||
Matcher matcher = PLACEHOLDER_PATTERN.matcher(content);
|
||||
while (matcher.find()) {
|
||||
String name = matcher.group(1);
|
||||
String value = matcher.group(2);
|
||||
if (properties != null && properties.containsKey(name)) {
|
||||
value = (String) properties.get(name);
|
||||
}
|
||||
else {
|
||||
value = (value == null ? matcher.group(0) : value.substring(1));
|
||||
}
|
||||
matcher.appendReplacement(expanded, value);
|
||||
}
|
||||
matcher.appendTail(expanded);
|
||||
return expanded.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] toByteArray() {
|
||||
return this.content.getBytes(UTF_8);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -27,6 +27,9 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.attribute.PosixFilePermission;
|
||||
import java.util.Arrays;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashSet;
|
||||
@@ -63,7 +66,37 @@ public class JarWriter {
|
||||
* @throws FileNotFoundException
|
||||
*/
|
||||
public JarWriter(File file) throws FileNotFoundException, IOException {
|
||||
this.jarOutput = new JarOutputStream(new FileOutputStream(file));
|
||||
this(file, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link JarWriter} instance.
|
||||
* @param file the file to write
|
||||
* @param launchScript an optional launch script to prepend to the front of the jar
|
||||
* @throws IOException
|
||||
* @throws FileNotFoundException
|
||||
*/
|
||||
public JarWriter(File file, LaunchScript launchScript) throws FileNotFoundException,
|
||||
IOException {
|
||||
FileOutputStream fileOutputStream = new FileOutputStream(file);
|
||||
if (launchScript != null) {
|
||||
fileOutputStream.write(launchScript.toByteArray());
|
||||
setExecutableFilePermission(file);
|
||||
}
|
||||
this.jarOutput = new JarOutputStream(fileOutputStream);
|
||||
}
|
||||
|
||||
private void setExecutableFilePermission(File file) {
|
||||
try {
|
||||
Path path = file.toPath();
|
||||
Set<PosixFilePermission> permissions = new HashSet<PosixFilePermission>(
|
||||
Files.getPosixFilePermissions(path));
|
||||
permissions.add(PosixFilePermission.OWNER_EXECUTE);
|
||||
Files.setPosixFilePermissions(path, permissions);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
// Ignore and continue creating the jar
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2012-2015 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.loader.tools;
|
||||
|
||||
/**
|
||||
* A script that can be prepended to the front of a JAR file to make it executable.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public interface LaunchScript {
|
||||
|
||||
/**
|
||||
* The the content of the launch script as a byte array.
|
||||
* @return the script bytes
|
||||
*/
|
||||
byte[] toByteArray();
|
||||
|
||||
}
|
||||
@@ -104,17 +104,29 @@ public class Repackager {
|
||||
* @throws IOException
|
||||
*/
|
||||
public void repackage(File destination, Libraries libraries) throws IOException {
|
||||
repackage(destination, libraries, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Repackage to the given destination so that it can be launched using '
|
||||
* {@literal java -jar}'
|
||||
* @param destination the destination file (may be the same as the source)
|
||||
* @param libraries the libraries required to run the archive
|
||||
* @param launchScript an optional launch script prepended to the front of the jar
|
||||
* @throws IOException
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public void repackage(File destination, Libraries libraries, LaunchScript launchScript)
|
||||
throws IOException {
|
||||
if (destination == null || destination.isDirectory()) {
|
||||
throw new IllegalArgumentException("Invalid destination");
|
||||
}
|
||||
if (libraries == null) {
|
||||
throw new IllegalArgumentException("Libraries must not be null");
|
||||
}
|
||||
|
||||
if (alreadyRepackaged()) {
|
||||
return;
|
||||
}
|
||||
|
||||
destination = destination.getAbsoluteFile();
|
||||
File workingSource = this.source;
|
||||
if (this.source.equals(destination)) {
|
||||
@@ -127,7 +139,7 @@ public class Repackager {
|
||||
try {
|
||||
JarFile jarFileSource = new JarFile(workingSource);
|
||||
try {
|
||||
repackage(jarFileSource, destination, libraries);
|
||||
repackage(jarFileSource, destination, libraries, launchScript);
|
||||
}
|
||||
finally {
|
||||
jarFileSource.close();
|
||||
@@ -152,9 +164,9 @@ public class Repackager {
|
||||
}
|
||||
}
|
||||
|
||||
private void repackage(JarFile sourceJar, File destination, Libraries libraries)
|
||||
throws IOException {
|
||||
final JarWriter writer = new JarWriter(destination);
|
||||
private void repackage(JarFile sourceJar, File destination, Libraries libraries,
|
||||
LaunchScript launchScript) throws IOException {
|
||||
final JarWriter writer = new JarWriter(destination, launchScript);
|
||||
try {
|
||||
final Set<String> seen = new HashSet<String>();
|
||||
writer.writeManifest(buildManifest(sourceJar));
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# . ____ _ __ _ _
|
||||
# /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
|
||||
# ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
|
||||
# \\/ ___)| |_)| | | | | || (_| | ) ) ) )
|
||||
# ' |____| .__|_| |_|_| |_\__, | / / / /
|
||||
# =========|_|==============|___/=/_/_/_/
|
||||
# :: Spring Boot Startup Script ::
|
||||
#
|
||||
|
||||
WORKING_DIR="$(pwd)"
|
||||
PID_FOLDER="/var/run"
|
||||
USER_PID_FOLDER="/tmp"
|
||||
LOG_FOLDER="/var/log"
|
||||
USER_LOG_FOLDER="/tmp"
|
||||
|
||||
# Setup defaults
|
||||
[[ -z "$mode" ]] && mode="{{mode:auto}}" # modes are "auto", "service" or "run"
|
||||
|
||||
# ANSI Colors
|
||||
echoRed() { echo $'\e[0;31m'$1$'\e[0m'; }
|
||||
echoGreen() { echo $'\e[0;32m'$1$'\e[0m'; }
|
||||
echoYellow() { echo $'\e[0;33m'$1$'\e[0m'; }
|
||||
|
||||
# Follow symlinks to find the real jar and detect init.d script
|
||||
cd $(dirname "$0")
|
||||
[[ -z "$jarfile" ]] && jarfile=$(pwd)/$(basename "$0")
|
||||
while [[ -L "$jarfile" ]]; do
|
||||
[[ "$jarfile" =~ "init.d" ]] && init_script=$(basename "$jarfile")
|
||||
jarfile=$(readlink "$jarfile")
|
||||
cd $(dirname "$jarfile")
|
||||
jarfile=$(pwd)/$(basename "$jarfile")
|
||||
done
|
||||
cd "$WORKING_DIR"
|
||||
|
||||
# Determine the script mode
|
||||
action="run"
|
||||
if [[ "$mode" == "auto" && -n "$init_script" ]] || [[ "$mode" == "service" ]]; then
|
||||
action="$1"
|
||||
shift
|
||||
fi
|
||||
|
||||
# Create an identity for log/pid files
|
||||
if [[ -n "$init_script" ]]; then
|
||||
identity="${init_script}"
|
||||
else
|
||||
jar_folder=$(dirname "$jarfile")
|
||||
identity=$(basename "${jarfile%.*}")_${jar_folder//\//}
|
||||
fi
|
||||
|
||||
# Build the pid and log filenames
|
||||
if [[ -n "$init_script" ]]; then
|
||||
pid_file="$PID_FOLDER/${identity}/${identity}.pid"
|
||||
log_file="$LOG_FOLDER/${identity}.log"
|
||||
else
|
||||
pid_file="$USER_PID_FOLDER/${identity}.pid"
|
||||
log_file="$USER_LOG_FOLDER/${identity}.log"
|
||||
fi
|
||||
|
||||
# Determine the user to run as
|
||||
[[ $(id -u) == "0" ]] && run_user=$(ls -ld "$jarfile" | awk '{print $3}')
|
||||
|
||||
# Find Java
|
||||
if type -p java 2>&1> /dev/null; then
|
||||
javaexe=java
|
||||
elif [[ -n "$JAVA_HOME" ]] && [[ -x "$JAVA_HOME/bin/java" ]]; then
|
||||
javaexe="$JAVA_HOME/bin/java"
|
||||
elif [[ -x "/usr/bin/java" ]]; then
|
||||
javaexe="/usr/bin/java"
|
||||
else
|
||||
echo "Unable to find Java"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Build actual command to execute
|
||||
command="$javaexe -jar -Dsun.misc.URLClassPath.disableJarChecking=true $jarfile $@"
|
||||
|
||||
# Utility functions
|
||||
checkPermissions() {
|
||||
touch "$pid_file" &> /dev/null || { echoRed "Operation not permitted (cannot access pid file)"; exit 1; }
|
||||
touch "$log_file" &> /dev/null || { echoRed "Operation not permitted (cannot access log file)"; exit 1; }
|
||||
}
|
||||
|
||||
isRunning() {
|
||||
ps -p $1 &> /dev/null
|
||||
}
|
||||
|
||||
# Action functions
|
||||
start() {
|
||||
if [[ -f "$pid_file" ]]; then
|
||||
pid=$(cat "$pid_file")
|
||||
isRunning $pid && { echoYellow "Already running [$pid]"; exit 0; }
|
||||
fi
|
||||
pushd $(dirname "$jarfile") > /dev/null
|
||||
if [[ -n "$run_user" ]]; then
|
||||
mkdir "$PID_FOLDER/${identity}" &> /dev/null
|
||||
checkPermissions
|
||||
chown "$run_user" "$PID_FOLDER/${identity}"
|
||||
chown "$run_user" "$pid_file"
|
||||
chown "$run_user" "$log_file"
|
||||
su -c "$command &> \"$log_file\" & echo \$!" $run_user > "$pid_file"
|
||||
pid=$(cat "$pid_file")
|
||||
else
|
||||
checkPermissions
|
||||
$command &> "$log_file" &
|
||||
pid=$!
|
||||
disown $pid
|
||||
echo "$pid" > "$pid_file"
|
||||
fi
|
||||
[[ -z $pid ]] && { echoRed "Failed to start"; exit 1; }
|
||||
echoGreen "Started [$pid]"
|
||||
}
|
||||
|
||||
stop() {
|
||||
[[ -f $pid_file ]] || { echoRed "Not running (pidfile not found)"; exit 1; }
|
||||
pid=$(cat "$pid_file")
|
||||
isRunning $pid || { echoRed "Not running (process ${pid} not found)"; exit 1; }
|
||||
kill -HUP $pid &> /dev/null || { echoRed "Unable to kill process ${pid}"; exit 1; }
|
||||
for i in $(seq 1 20); do
|
||||
isRunning ${pid} || { echoGreen "Stopped [$pid]"; rm -f $pid_file; exit 0; }
|
||||
sleep 1
|
||||
done
|
||||
echoRed "Unable to kill process ${pid}";
|
||||
exit 3;
|
||||
}
|
||||
|
||||
restart() {
|
||||
stop
|
||||
start
|
||||
}
|
||||
|
||||
status() {
|
||||
[[ -f $pid_file ]] || { echoRed "Not running"; exit 1; }
|
||||
pid=$(cat "$pid_file")
|
||||
isRunning $pid || { echoRed "Not running (process ${pid} not found)"; exit 1; }
|
||||
echoGreen "Running [$pid]"
|
||||
exit 0
|
||||
}
|
||||
|
||||
run() {
|
||||
pushd $(dirname "$jarfile") > /dev/null
|
||||
exec $command
|
||||
popd
|
||||
}
|
||||
|
||||
# Call the appropriate action function
|
||||
case "$action" in
|
||||
start)
|
||||
start "$@";;
|
||||
stop)
|
||||
stop "$@";;
|
||||
restart)
|
||||
restart "$@";;
|
||||
status)
|
||||
status "$@";;
|
||||
run)
|
||||
run "$@";;
|
||||
*)
|
||||
echo "Usage: $0 {start|stop|restart|status|run}"; exit 1;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright 2012-2015 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.loader.tools;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link DefaultLaunchScript}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class DefaultLaunchScriptTests {
|
||||
|
||||
@Rule
|
||||
public TemporaryFolder temporaryFolder = new TemporaryFolder();
|
||||
|
||||
@Test
|
||||
public void loadsDefaultScript() throws Exception {
|
||||
DefaultLaunchScript script = new DefaultLaunchScript(null, null);
|
||||
String content = new String(script.toByteArray());
|
||||
assertThat(content, containsString("Spring Boot Startup Script"));
|
||||
assertThat(content, containsString("mode=\"auto\""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadFromFile() throws Exception {
|
||||
File file = this.temporaryFolder.newFile();
|
||||
FileCopyUtils.copy("ABC".getBytes(), file);
|
||||
DefaultLaunchScript script = new DefaultLaunchScript(file, null);
|
||||
String content = new String(script.toByteArray());
|
||||
assertThat(content, equalTo("ABC"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void expandVariables() throws Exception {
|
||||
File file = this.temporaryFolder.newFile();
|
||||
FileCopyUtils.copy("h{{a}}ll{{b}}".getBytes(), file);
|
||||
Properties properties = new Properties();
|
||||
properties.put("a", "e");
|
||||
properties.put("b", "o");
|
||||
DefaultLaunchScript script = new DefaultLaunchScript(file, properties);
|
||||
String content = new String(script.toByteArray());
|
||||
assertThat(content, equalTo("hello"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void expandVariablesMultiLine() throws Exception {
|
||||
File file = this.temporaryFolder.newFile();
|
||||
FileCopyUtils.copy("h{{a}}l\nl{{b}}".getBytes(), file);
|
||||
Properties properties = new Properties();
|
||||
properties.put("a", "e");
|
||||
properties.put("b", "o");
|
||||
DefaultLaunchScript script = new DefaultLaunchScript(file, properties);
|
||||
String content = new String(script.toByteArray());
|
||||
assertThat(content, equalTo("hel\nlo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void expandVariablesWithDefaults() throws Exception {
|
||||
File file = this.temporaryFolder.newFile();
|
||||
FileCopyUtils.copy("h{{a:e}}ll{{b:o}}".getBytes(), file);
|
||||
DefaultLaunchScript script = new DefaultLaunchScript(file, null);
|
||||
String content = new String(script.toByteArray());
|
||||
assertThat(content, equalTo("hello"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void expandVariablesWithDefaultsOverride() throws Exception {
|
||||
File file = this.temporaryFolder.newFile();
|
||||
FileCopyUtils.copy("h{{a:e}}ll{{b:o}}".getBytes(), file);
|
||||
Properties properties = new Properties();
|
||||
properties.put("a", "a");
|
||||
DefaultLaunchScript script = new DefaultLaunchScript(file, properties);
|
||||
String content = new String(script.toByteArray());
|
||||
assertThat(content, equalTo("hallo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void expandVariablesMissingAreUnchanged() throws Exception {
|
||||
File file = this.temporaryFolder.newFile();
|
||||
FileCopyUtils.copy("h{{a}}ll{{b}}".getBytes(), file);
|
||||
DefaultLaunchScript script = new DefaultLaunchScript(file, null);
|
||||
String content = new String(script.toByteArray());
|
||||
assertThat(content, equalTo("h{{a}}ll{{b}}"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,6 +18,8 @@ package org.springframework.boot.loader.tools;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.attribute.PosixFilePermission;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
@@ -34,6 +36,7 @@ import org.springframework.boot.loader.tools.sample.ClassWithoutMainMethod;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.hasItem;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
@@ -141,7 +144,6 @@ public class RepackagerTests {
|
||||
Repackager repackager = new Repackager(file);
|
||||
repackager.repackage(NO_LIBRARIES);
|
||||
repackager.repackage(NO_LIBRARIES);
|
||||
|
||||
Manifest actualManifest = getManifest(file);
|
||||
assertThat(actualManifest.getMainAttributes().getValue("Main-Class"),
|
||||
equalTo("org.springframework.boot.loader.JarLauncher"));
|
||||
@@ -230,7 +232,6 @@ public class RepackagerTests {
|
||||
equalTo(false));
|
||||
assertThat(hasLauncherClasses(source), equalTo(false));
|
||||
assertThat(hasLauncherClasses(dest), equalTo(true));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -380,7 +381,6 @@ public class RepackagerTests {
|
||||
callback.library(new Library(nestedFile, LibraryScope.COMPILE));
|
||||
}
|
||||
});
|
||||
|
||||
JarFile jarFile = new JarFile(file);
|
||||
try {
|
||||
assertThat(jarFile.getEntry("lib/" + nestedFile.getName()).getMethod(),
|
||||
@@ -393,6 +393,22 @@ public class RepackagerTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addLauncherScript() throws Exception {
|
||||
this.testJarFile.addClass("a/b/C.class", ClassWithMainMethod.class);
|
||||
File source = this.testJarFile.getFile();
|
||||
File dest = this.temporaryFolder.newFile("dest.jar");
|
||||
Repackager repackager = new Repackager(source);
|
||||
LaunchScript script = new MockLauncherScript("ABC");
|
||||
repackager.repackage(dest, NO_LIBRARIES, script);
|
||||
byte[] bytes = FileCopyUtils.copyToByteArray(dest);
|
||||
assertThat(Files.getPosixFilePermissions(dest.toPath()),
|
||||
hasItem(PosixFilePermission.OWNER_EXECUTE));
|
||||
assertThat(new String(bytes), startsWith("ABC"));
|
||||
assertThat(hasLauncherClasses(source), equalTo(false));
|
||||
assertThat(hasLauncherClasses(dest), equalTo(true));
|
||||
}
|
||||
|
||||
private boolean hasLauncherClasses(File file) throws IOException {
|
||||
return hasEntry(file, "org/springframework/boot/")
|
||||
&& hasEntry(file, "org/springframework/boot/loader/JarLauncher.class");
|
||||
@@ -422,4 +438,19 @@ public class RepackagerTests {
|
||||
}
|
||||
}
|
||||
|
||||
private static class MockLauncherScript implements LaunchScript {
|
||||
|
||||
private final byte[] bytes;
|
||||
|
||||
public MockLauncherScript(String script) {
|
||||
this.bytes = script.getBytes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] toByteArray() {
|
||||
return this.bytes;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user