/*
* Copyright 2012-2017 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.gradle.tasks.bundling;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import org.gradle.api.Project;
import org.gradle.api.tasks.bundling.AbstractArchiveTask;
import org.gradle.api.tasks.bundling.Jar;
import org.gradle.testfixtures.ProjectBuilder;
import org.gradle.util.GUtil;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.springframework.boot.loader.tools.DefaultLaunchScript;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Abstract base class for testing {@link BootArchive} implementations.
*
* @param <T> the type of the concrete BootArchive implementation
* @author Andy Wilkinson
*/
public abstract class AbstractBootArchiveTests<T extends Jar & BootArchive> {
@Rule
public final TemporaryFolder temp = new TemporaryFolder();
private final Class<T> taskClass;
private final String launcherClass;
private final String libPath;
private final String classesPath;
private T task;
protected AbstractBootArchiveTests(Class<T> taskClass, String launcherClass,
String libPath, String classesPath) {
this.taskClass = taskClass;
this.launcherClass = launcherClass;
this.libPath = libPath;
this.classesPath = classesPath;
}
@Before
public void createTask() {
try {
Project project = ProjectBuilder.builder()
.withProjectDir(this.temp.newFolder()).build();
this.task = configure(
project.getTasks().create("testArchive", this.taskClass));
}
catch (IOException ex) {
throw new RuntimeException(ex);
}
}
@Test
public void basicArchiveCreation() throws IOException {
this.task.setMainClass("com.example.Main");
this.task.execute();
assertThat(this.task.getArchivePath().exists());
try (JarFile jarFile = new JarFile(this.task.getArchivePath())) {
assertThat(jarFile.getManifest().getMainAttributes().getValue("Main-Class"))
.isEqualTo(this.launcherClass);
assertThat(jarFile.getManifest().getMainAttributes().getValue("Start-Class"))
.isEqualTo("com.example.Main");
}
}
@Test
public void classpathJarsArePackagedBeneathLibPath() throws IOException {
this.task.setMainClass("com.example.Main");
this.task.classpath(this.temp.newFile("one.jar"), this.temp.newFile("two.jar"));
this.task.execute();
try (JarFile jarFile = new JarFile(this.task.getArchivePath())) {
assertThat(jarFile.getEntry(this.libPath + "/one.jar")).isNotNull();
assertThat(jarFile.getEntry(this.libPath + "/two.jar")).isNotNull();
}
}
@Test
public void classpathFoldersArePackagedBeneathClassesPath() throws IOException {
this.task.setMainClass("com.example.Main");
File classpathFolder = this.temp.newFolder();
File applicationClass = new File(classpathFolder,
"com/example/Application.class");
applicationClass.getParentFile().mkdirs();
applicationClass.createNewFile();
this.task.classpath(classpathFolder);
this.task.execute();
try (JarFile jarFile = new JarFile(this.task.getArchivePath())) {
assertThat(
jarFile.getEntry(this.classesPath + "/com/example/Application.class"))
.isNotNull();
}
}
@Test
public void loaderIsWrittenToTheRootOfTheJar() throws IOException {
this.task.setMainClass("com.example.Main");
this.task.execute();
try (JarFile jarFile = new JarFile(this.task.getArchivePath())) {
assertThat(jarFile.getEntry(
"org/springframework/boot/loader/LaunchedURLClassLoader.class"))
.isNotNull();
assertThat(jarFile.getEntry("org/springframework/boot/loader/")).isNotNull();
}
}
@Test
public void loaderIsWrittenToTheRootOfTheJarWhenUsingThePropertiesLauncher()
throws IOException {
this.task.setMainClass("com.example.Main");
this.task.execute();
this.task.getManifest().getAttributes().put("Main-Class",
"org.springframework.boot.loader.PropertiesLauncher");
try (JarFile jarFile = new JarFile(this.task.getArchivePath())) {
assertThat(jarFile.getEntry(
"org/springframework/boot/loader/LaunchedURLClassLoader.class"))
.isNotNull();
assertThat(jarFile.getEntry("org/springframework/boot/loader/")).isNotNull();
}
}
@Test
public void unpackCommentIsAddedToEntryIdentifiedByAPattern() throws IOException {
this.task.setMainClass("com.example.Main");
this.task.classpath(this.temp.newFile("one.jar"), this.temp.newFile("two.jar"));
this.task.requiresUnpack("**/one.jar");
this.task.execute();
try (JarFile jarFile = new JarFile(this.task.getArchivePath())) {
assertThat(jarFile.getEntry(this.libPath + "/one.jar").getComment())
.startsWith("UNPACK:");
assertThat(jarFile.getEntry(this.libPath + "/two.jar").getComment()).isNull();
}
}
@Test
public void unpackCommentIsAddedToEntryIdentifiedByASpec() throws IOException {
this.task.setMainClass("com.example.Main");
this.task.classpath(this.temp.newFile("one.jar"), this.temp.newFile("two.jar"));
this.task.requiresUnpack((element) -> element.getName().endsWith("two.jar"));
this.task.execute();
try (JarFile jarFile = new JarFile(this.task.getArchivePath())) {
assertThat(jarFile.getEntry(this.libPath + "/two.jar").getComment())
.startsWith("UNPACK:");
assertThat(jarFile.getEntry(this.libPath + "/one.jar").getComment()).isNull();
}
}
@Test
public void launchScriptCanBePrepended() throws IOException {
this.task.setMainClass("com.example.Main");
this.task.getLaunchScript().setIncluded(true);
this.task.execute();
assertThat(Files.readAllBytes(this.task.getArchivePath().toPath()))
.startsWith(new DefaultLaunchScript(null, null).toByteArray());
}
@Test
public void customLaunchScriptCanBePrepended() throws IOException {
this.task.setMainClass("com.example.Main");
LaunchScriptConfiguration launchScript = this.task.getLaunchScript();
launchScript.setIncluded(true);
File customScript = this.temp.newFile("custom.script");
Files.write(customScript.toPath(), Arrays.asList("custom script"),
StandardOpenOption.CREATE);
launchScript.setScript(customScript);
this.task.execute();
assertThat(Files.readAllBytes(this.task.getArchivePath().toPath()))
.startsWith("custom script".getBytes());
}
@Test
public void launchScriptPropertiesAreReplaced() throws IOException {
this.task.setMainClass("com.example.Main");
LaunchScriptConfiguration launchScript = this.task.getLaunchScript();
launchScript.setIncluded(true);
launchScript.getProperties().put("initInfoProvides", "test property value");
this.task.execute();
assertThat(Files.readAllBytes(this.task.getArchivePath().toPath()))
.containsSequence("test property value".getBytes());
}
@Test
public void customMainClassInTheManifestIsHonored() throws IOException {
this.task.setMainClass("com.example.Main");
this.task.getManifest().getAttributes().put("Main-Class",
"com.example.CustomLauncher");
this.task.execute();
assertThat(this.task.getArchivePath().exists());
try (JarFile jarFile = new JarFile(this.task.getArchivePath())) {
assertThat(jarFile.getManifest().getMainAttributes().getValue("Main-Class"))
.isEqualTo("com.example.CustomLauncher");
assertThat(jarFile.getManifest().getMainAttributes().getValue("Start-Class"))
.isEqualTo("com.example.Main");
assertThat(jarFile.getEntry(
"org/springframework/boot/loader/LaunchedURLClassLoader.class"))
.isNull();
}
}
@Test
public void customStartClassInTheManifestIsHonored() throws IOException {
this.task.setMainClass("com.example.Main");
this.task.getManifest().getAttributes().put("Start-Class",
"com.example.CustomMain");
this.task.execute();
assertThat(this.task.getArchivePath().exists());
try (JarFile jarFile = new JarFile(this.task.getArchivePath())) {
assertThat(jarFile.getManifest().getMainAttributes().getValue("Main-Class"))
.isEqualTo(this.launcherClass);
assertThat(jarFile.getManifest().getMainAttributes().getValue("Start-Class"))
.isEqualTo("com.example.CustomMain");
}
}
@Test
public void fileTimestampPreservationCanBeDisabled() throws IOException {
this.task.setMainClass("com.example.Main");
this.task.setPreserveFileTimestamps(false);
this.task.execute();
assertThat(this.task.getArchivePath().exists());
try (JarFile jarFile = new JarFile(this.task.getArchivePath())) {
Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
assertThat(entry.getTime())
.isEqualTo(GUtil.CONSTANT_TIME_FOR_ZIP_ENTRIES);
}
}
}
@Test
public void reproducibleOrderingCanBeEnabled() throws IOException {
this.task.setMainClass("com.example.Main");
this.task.from(this.temp.newFile("bravo.txt"), this.temp.newFile("alpha.txt"),
this.temp.newFile("charlie.txt"));
this.task.setReproducibleFileOrder(true);
this.task.execute();
assertThat(this.task.getArchivePath().exists());
List<String> textFiles = new ArrayList<>();
try (JarFile jarFile = new JarFile(this.task.getArchivePath())) {
Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
if (entry.getName().endsWith(".txt")) {
textFiles.add(entry.getName());
}
}
}
assertThat(textFiles).containsExactly("alpha.txt", "bravo.txt", "charlie.txt");
}
@Test
public void devtoolsJarIsExcludedByDefault() throws IOException {
this.task.setMainClass("com.example.Main");
this.task.classpath(this.temp.newFile("spring-boot-devtools-0.1.2.jar"));
this.task.execute();
assertThat(this.task.getArchivePath().exists());
try (JarFile jarFile = new JarFile(this.task.getArchivePath())) {
assertThat(jarFile.getEntry(this.libPath + "/spring-boot-devtools-0.1.2.jar"))
.isNull();
}
}
@Test
public void devtoolsJarCanBeIncluded() throws IOException {
this.task.setMainClass("com.example.Main");
this.task.classpath(this.temp.newFile("spring-boot-devtools-0.1.2.jar"));
this.task.setExcludeDevtools(false);
this.task.execute();
assertThat(this.task.getArchivePath().exists());
try (JarFile jarFile = new JarFile(this.task.getArchivePath())) {
assertThat(jarFile.getEntry(this.libPath + "/spring-boot-devtools-0.1.2.jar"))
.isNotNull();
}
}
private T configure(T task) throws IOException {
AbstractArchiveTask archiveTask = task;
archiveTask.setBaseName("test");
archiveTask.setDestinationDir(this.temp.newFolder());
return task;
}
protected T getTask() {
return this.task;
}
}