Rework spring-boot-server-tests to avoid using Maven
Closes gh-27476
This commit is contained in:
parent
ee07d6c3ca
commit
d9a24f32b4
|
@ -1,29 +1,24 @@
|
||||||
plugins {
|
plugins {
|
||||||
id "java"
|
id "java"
|
||||||
id "org.springframework.boot.conventions"
|
id "org.springframework.boot.conventions"
|
||||||
|
id "org.springframework.boot.integration-test"
|
||||||
}
|
}
|
||||||
|
|
||||||
description = "Spring Boot Server Tests"
|
description = "Spring Boot Server Integration Tests"
|
||||||
|
|
||||||
configurations {
|
configurations {
|
||||||
testRepository
|
testRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
testImplementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-test"))
|
intTestImplementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-test"))
|
||||||
testImplementation(project(":spring-boot-project:spring-boot-tools:spring-boot-test-support"))
|
intTestImplementation(project(":spring-boot-project:spring-boot-tools:spring-boot-test-support"))
|
||||||
testImplementation("com.samskivert:jmustache")
|
intTestImplementation("org.apache.httpcomponents:httpasyncclient")
|
||||||
testImplementation("jakarta.servlet:jakarta.servlet-api")
|
intTestImplementation("org.awaitility:awaitility")
|
||||||
testImplementation("org.apache.httpcomponents:httpasyncclient")
|
intTestImplementation("org.springframework:spring-web")
|
||||||
testImplementation("org.apache.maven.shared:maven-invoker:3.0.0")
|
|
||||||
testImplementation("org.awaitility:awaitility")
|
|
||||||
testImplementation("org.eclipse.jetty:jetty-webapp") {
|
|
||||||
exclude group: "javax.servlet", module: "javax-servlet-api"
|
|
||||||
}
|
|
||||||
testImplementation("org.springframework:spring-web")
|
|
||||||
|
|
||||||
testRepository(project(path: ":spring-boot-project:spring-boot-dependencies", configuration: "mavenRepository"))
|
testRepository(project(path: ":spring-boot-project:spring-boot-dependencies", configuration: "mavenRepository"))
|
||||||
testRepository(project(path: ":spring-boot-project:spring-boot-tools:spring-boot-maven-plugin", configuration: "mavenRepository"))
|
testRepository(project(path: ":spring-boot-project:spring-boot-tools:spring-boot-gradle-plugin", configuration: "mavenRepository"))
|
||||||
testRepository(project(path: ":spring-boot-project:spring-boot-starters:spring-boot-starter", configuration: "mavenRepository"))
|
testRepository(project(path: ":spring-boot-project:spring-boot-starters:spring-boot-starter", configuration: "mavenRepository"))
|
||||||
testRepository(project(path: ":spring-boot-project:spring-boot-starters:spring-boot-starter-jetty", configuration: "mavenRepository"))
|
testRepository(project(path: ":spring-boot-project:spring-boot-starters:spring-boot-starter-jetty", configuration: "mavenRepository"))
|
||||||
testRepository(project(path: ":spring-boot-project:spring-boot-starters:spring-boot-starter-json", configuration: "mavenRepository"))
|
testRepository(project(path: ":spring-boot-project:spring-boot-starters:spring-boot-starter-json", configuration: "mavenRepository"))
|
||||||
|
@ -34,11 +29,6 @@ dependencies {
|
||||||
testRuntimeOnly(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-logging"))
|
testRuntimeOnly(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-logging"))
|
||||||
}
|
}
|
||||||
|
|
||||||
task prepareMavenBinaries(type: org.springframework.boot.build.mavenplugin.PrepareMavenBinaries) {
|
|
||||||
outputDir = file("${buildDir}/maven-binaries")
|
|
||||||
versions "3.6.2"
|
|
||||||
}
|
|
||||||
|
|
||||||
task syncTestRepository(type: Sync) {
|
task syncTestRepository(type: Sync) {
|
||||||
destinationDir = file("${buildDir}/test-repository")
|
destinationDir = file("${buildDir}/test-repository")
|
||||||
from {
|
from {
|
||||||
|
@ -46,6 +36,28 @@ task syncTestRepository(type: Sync) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
test {
|
task syncAppSource(type: Sync) {
|
||||||
dependsOn prepareMavenBinaries, syncTestRepository
|
from "spring-boot-server-tests-app"
|
||||||
|
into "${buildDir}/spring-boot-server-tests-app"
|
||||||
|
filter { line ->
|
||||||
|
line.replace("id \"org.springframework.boot\"", "id \"org.springframework.boot\" version \"${project.version}\"")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task buildApps(type: GradleBuild) {
|
||||||
|
dependsOn syncAppSource, syncTestRepository
|
||||||
|
dir = "${buildDir}/spring-boot-server-tests-app"
|
||||||
|
startParameter.buildCacheEnabled = false
|
||||||
|
tasks = [
|
||||||
|
"jettyBootJar",
|
||||||
|
"jettyBootWar",
|
||||||
|
"tomcatBootJar",
|
||||||
|
"tomcatBootWar",
|
||||||
|
"undertowBootJar",
|
||||||
|
"undertowBootWar"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
intTest {
|
||||||
|
dependsOn buildApps
|
||||||
}
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
import org.springframework.boot.gradle.tasks.bundling.BootJar
|
||||||
|
import org.springframework.boot.gradle.tasks.bundling.BootWar
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id "java"
|
||||||
|
id "org.springframework.boot"
|
||||||
|
id "war"
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: "io.spring.dependency-management"
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
maven { url "file:${rootDir}/../test-repository"}
|
||||||
|
mavenCentral()
|
||||||
|
maven {
|
||||||
|
url "https://repo.spring.io/milestone"
|
||||||
|
content {
|
||||||
|
excludeGroup "org.springframework.boot"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
maven {
|
||||||
|
url "https://repo.spring.io/snapshot"
|
||||||
|
content {
|
||||||
|
excludeGroup "org.springframework.boot"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
configurations {
|
||||||
|
jetty
|
||||||
|
tomcat
|
||||||
|
undertow
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register("resourcesJar", Jar) { jar ->
|
||||||
|
def nested = project.resources.text.fromString("nested")
|
||||||
|
from(nested) {
|
||||||
|
into "META-INF/resources/"
|
||||||
|
rename (".*", "nested-meta-inf-resource.txt")
|
||||||
|
}
|
||||||
|
if (!isWindows()) {
|
||||||
|
def encodedName = project.resources.text.fromString("encoded-name")
|
||||||
|
from(encodedName) {
|
||||||
|
into "META-INF/resources/"
|
||||||
|
rename (".*", 'nested-reserved-!#\\$%&()*+,:=?@[]-meta-inf-resource.txt')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
classifier = 'resources'
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compileOnly("org.eclipse.jetty:jetty-server")
|
||||||
|
|
||||||
|
implementation("org.springframework.boot:spring-boot-starter")
|
||||||
|
implementation("org.springframework:spring-web")
|
||||||
|
|
||||||
|
jetty("org.springframework.boot:spring-boot-starter-jetty")
|
||||||
|
jetty files(resourcesJar)
|
||||||
|
tomcat("org.springframework.boot:spring-boot-starter-tomcat")
|
||||||
|
tomcat files(resourcesJar)
|
||||||
|
undertow("org.springframework.boot:spring-boot-starter-undertow")
|
||||||
|
undertow files(resourcesJar)
|
||||||
|
}
|
||||||
|
|
||||||
|
def boolean isWindows() {
|
||||||
|
return File.separatorChar == '\\';
|
||||||
|
}
|
||||||
|
|
||||||
|
["jetty", "tomcat", "undertow"].each { container ->
|
||||||
|
def configurer = { task ->
|
||||||
|
task.dependsOn resourcesJar
|
||||||
|
task.mainClass = "com.example.ResourceHandlingApplication"
|
||||||
|
task.classpath = sourceSets.main.runtimeClasspath.plus(configurations.getByName(container))
|
||||||
|
task.classifier = container
|
||||||
|
}
|
||||||
|
tasks.register("${container}BootJar", BootJar, configurer)
|
||||||
|
tasks.register("${container}BootWar", BootWar, configurer)
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
pluginManagement {
|
||||||
|
repositories {
|
||||||
|
maven { url "file:${rootDir}/../test-repository"}
|
||||||
|
mavenCentral()
|
||||||
|
maven {
|
||||||
|
url "https://repo.spring.io/milestone"
|
||||||
|
content {
|
||||||
|
excludeGroup "org.springframework.boot"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
maven {
|
||||||
|
url "https://repo.spring.io/snapshot"
|
||||||
|
content {
|
||||||
|
excludeGroup "org.springframework.boot"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resolutionStrategy {
|
||||||
|
eachPlugin {
|
||||||
|
if (requested.id.id == "org.springframework.boot") {
|
||||||
|
useModule "org.springframework.boot:spring-boot-gradle-plugin:${requested.version}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,6 +18,7 @@ package com.example;
|
||||||
|
|
||||||
import org.eclipse.jetty.server.handler.ContextHandler;
|
import org.eclipse.jetty.server.handler.ContextHandler;
|
||||||
|
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||||
import org.springframework.boot.web.embedded.jetty.JettyServerCustomizer;
|
import org.springframework.boot.web.embedded.jetty.JettyServerCustomizer;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
@ -28,6 +29,7 @@ import org.springframework.context.annotation.Configuration;
|
||||||
*
|
*
|
||||||
* @author Madhura Bhave
|
* @author Madhura Bhave
|
||||||
*/
|
*/
|
||||||
|
@ConditionalOnClass(name = {"org.eclipse.jetty.server.handler.ContextHandler"})
|
||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
public class JettyServerCustomizerConfig {
|
public class JettyServerCustomizerConfig {
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
webapp resource
|
|
@ -41,7 +41,7 @@ import org.springframework.util.StringUtils;
|
||||||
*/
|
*/
|
||||||
abstract class AbstractApplicationLauncher implements BeforeEachCallback {
|
abstract class AbstractApplicationLauncher implements BeforeEachCallback {
|
||||||
|
|
||||||
private final ApplicationBuilder applicationBuilder;
|
private final Application application;
|
||||||
|
|
||||||
private final BuildOutput buildOutput;
|
private final BuildOutput buildOutput;
|
||||||
|
|
||||||
|
@ -49,8 +49,8 @@ abstract class AbstractApplicationLauncher implements BeforeEachCallback {
|
||||||
|
|
||||||
private int httpPort;
|
private int httpPort;
|
||||||
|
|
||||||
protected AbstractApplicationLauncher(ApplicationBuilder applicationBuilder, BuildOutput buildOutput) {
|
protected AbstractApplicationLauncher(Application application, BuildOutput buildOutput) {
|
||||||
this.applicationBuilder = applicationBuilder;
|
this.application = application;
|
||||||
this.buildOutput = buildOutput;
|
this.buildOutput = buildOutput;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,7 +87,8 @@ abstract class AbstractApplicationLauncher implements BeforeEachCallback {
|
||||||
File workingDirectory = getWorkingDirectory();
|
File workingDirectory = getWorkingDirectory();
|
||||||
File serverPortFile = new File(this.buildOutput.getRootLocation(), "server.port");
|
File serverPortFile = new File(this.buildOutput.getRootLocation(), "server.port");
|
||||||
serverPortFile.delete();
|
serverPortFile.delete();
|
||||||
File archive = this.applicationBuilder.buildApplication();
|
File archive = new File("build/spring-boot-server-tests-app/build/libs/spring-boot-server-tests-app-"
|
||||||
|
+ this.application.getContainer() + "." + this.application.getPackaging());
|
||||||
List<String> arguments = new ArrayList<>();
|
List<String> arguments = new ArrayList<>();
|
||||||
arguments.add(System.getProperty("java.home") + "/bin/java");
|
arguments.add(System.getProperty("java.home") + "/bin/java");
|
||||||
arguments.addAll(getArguments(archive, serverPortFile));
|
arguments.addAll(getArguments(archive, serverPortFile));
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2012-2020 the original author or authors.
|
* Copyright 2012-2021 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -16,29 +16,35 @@
|
||||||
|
|
||||||
package org.springframework.boot.context.embedded;
|
package org.springframework.boot.context.embedded;
|
||||||
|
|
||||||
import java.io.FileInputStream;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Properties;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides access to the current Boot version by referring to {@code gradle.properties}.
|
* An pre-built application that can be launched.
|
||||||
*
|
*
|
||||||
* @author Andy Wilkinson
|
* @author Andy Wilkinson
|
||||||
*/
|
*/
|
||||||
final class Versions {
|
class Application {
|
||||||
|
|
||||||
private Versions() {
|
private final String packaging;
|
||||||
|
|
||||||
|
private final String container;
|
||||||
|
|
||||||
|
Application(String packaging, String container) {
|
||||||
|
this.packaging = packaging;
|
||||||
|
this.container = container;
|
||||||
}
|
}
|
||||||
|
|
||||||
static String getBootVersion() {
|
String getPackaging() {
|
||||||
Properties gradleProperties = new Properties();
|
return this.packaging;
|
||||||
try (FileInputStream input = new FileInputStream("../../../gradle.properties")) {
|
|
||||||
gradleProperties.load(input);
|
|
||||||
return gradleProperties.getProperty("version");
|
|
||||||
}
|
}
|
||||||
catch (IOException ex) {
|
|
||||||
throw new RuntimeException(ex);
|
String getContainer() {
|
||||||
|
return this.container;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
File getArchive() {
|
||||||
|
return new File("build/spring-boot-server-tests-app/build/libs/spring-boot-server-tests-app-" + this.container
|
||||||
|
+ "." + this.packaging);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -43,8 +43,8 @@ class BootRunApplicationLauncher extends AbstractApplicationLauncher {
|
||||||
|
|
||||||
private final File exploded;
|
private final File exploded;
|
||||||
|
|
||||||
BootRunApplicationLauncher(ApplicationBuilder applicationBuilder, BuildOutput buildOutput) {
|
BootRunApplicationLauncher(Application application, BuildOutput buildOutput) {
|
||||||
super(applicationBuilder, buildOutput);
|
super(application, buildOutput);
|
||||||
this.exploded = new File(buildOutput.getRootLocation(), "run");
|
this.exploded = new File(buildOutput.getRootLocation(), "run");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,7 +112,7 @@ class BootRunApplicationLauncher extends AbstractApplicationLauncher {
|
||||||
|
|
||||||
private List<String> getLibPaths(File archive) {
|
private List<String> getLibPaths(File archive) {
|
||||||
return (archive.getName().endsWith(".jar") ? Collections.singletonList("BOOT-INF/lib")
|
return (archive.getName().endsWith(".jar") ? Collections.singletonList("BOOT-INF/lib")
|
||||||
: Arrays.asList("WEB-INF/lib", "WEB-INF/lib-provided"));
|
: Arrays.asList("WEB-INF/lib"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void explodeArchive(File archive) throws IOException {
|
private void explodeArchive(File archive) throws IOException {
|
|
@ -63,8 +63,6 @@ class EmbeddedServerContainerInvocationContextProvider
|
||||||
private static final BuildOutput buildOutput = new BuildOutput(
|
private static final BuildOutput buildOutput = new BuildOutput(
|
||||||
EmbeddedServerContainerInvocationContextProvider.class);
|
EmbeddedServerContainerInvocationContextProvider.class);
|
||||||
|
|
||||||
private final Map<String, ApplicationBuilder> builderCache = new HashMap<>();
|
|
||||||
|
|
||||||
private final Map<String, AbstractApplicationLauncher> launcherCache = new HashMap<>();
|
private final Map<String, AbstractApplicationLauncher> launcherCache = new HashMap<>();
|
||||||
|
|
||||||
private final Path tempDir;
|
private final Path tempDir;
|
||||||
|
@ -84,7 +82,7 @@ class EmbeddedServerContainerInvocationContextProvider
|
||||||
.getAnnotation(EmbeddedServletContainerTest.class);
|
.getAnnotation(EmbeddedServletContainerTest.class);
|
||||||
return CONTAINERS
|
return CONTAINERS
|
||||||
.stream().map(
|
.stream().map(
|
||||||
(container) -> getApplicationBuilder(annotation, container))
|
(container) -> getApplication(annotation, container))
|
||||||
.flatMap(
|
.flatMap(
|
||||||
(builder) -> Stream
|
(builder) -> Stream
|
||||||
.of(annotation.launchers()).map(
|
.of(annotation.launchers()).map(
|
||||||
|
@ -104,28 +102,21 @@ class EmbeddedServerContainerInvocationContextProvider
|
||||||
private void cleanupCaches() {
|
private void cleanupCaches() {
|
||||||
this.launcherCache.values().forEach(AbstractApplicationLauncher::destroyProcess);
|
this.launcherCache.values().forEach(AbstractApplicationLauncher::destroyProcess);
|
||||||
this.launcherCache.clear();
|
this.launcherCache.clear();
|
||||||
this.builderCache.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private AbstractApplicationLauncher getAbstractApplicationLauncher(ApplicationBuilder builder,
|
private AbstractApplicationLauncher getAbstractApplicationLauncher(Application application,
|
||||||
Class<? extends AbstractApplicationLauncher> launcherClass) {
|
Class<? extends AbstractApplicationLauncher> launcherClass) {
|
||||||
String cacheKey = builder.getContainer() + ":" + builder.getPackaging() + ":" + launcherClass.getName();
|
String cacheKey = application.getContainer() + ":" + application.getPackaging() + ":" + launcherClass.getName();
|
||||||
if (this.launcherCache.containsKey(cacheKey)) {
|
if (this.launcherCache.containsKey(cacheKey)) {
|
||||||
return this.launcherCache.get(cacheKey);
|
return this.launcherCache.get(cacheKey);
|
||||||
}
|
}
|
||||||
AbstractApplicationLauncher launcher = ReflectionUtils.newInstance(launcherClass, builder, buildOutput);
|
AbstractApplicationLauncher launcher = ReflectionUtils.newInstance(launcherClass, application, buildOutput);
|
||||||
this.launcherCache.put(cacheKey, launcher);
|
this.launcherCache.put(cacheKey, launcher);
|
||||||
return launcher;
|
return launcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ApplicationBuilder getApplicationBuilder(EmbeddedServletContainerTest annotation, String container) {
|
private Application getApplication(EmbeddedServletContainerTest annotation, String container) {
|
||||||
String cacheKey = container + ":" + annotation.packaging();
|
return new Application(annotation.packaging(), container);
|
||||||
if (this.builderCache.containsKey(cacheKey)) {
|
|
||||||
return this.builderCache.get(cacheKey);
|
|
||||||
}
|
|
||||||
ApplicationBuilder builder = new ApplicationBuilder(this.tempDir, annotation.packaging(), container);
|
|
||||||
this.builderCache.put(cacheKey, builder);
|
|
||||||
return builder;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static class EmbeddedServletContainerInvocationContext implements TestTemplateInvocationContext, ParameterResolver {
|
static class EmbeddedServletContainerInvocationContext implements TestTemplateInvocationContext, ParameterResolver {
|
|
@ -40,8 +40,8 @@ class ExplodedApplicationLauncher extends AbstractApplicationLauncher {
|
||||||
|
|
||||||
private final Supplier<File> exploded;
|
private final Supplier<File> exploded;
|
||||||
|
|
||||||
ExplodedApplicationLauncher(ApplicationBuilder applicationBuilder, BuildOutput buildOutput) {
|
ExplodedApplicationLauncher(Application application, BuildOutput buildOutput) {
|
||||||
super(applicationBuilder, buildOutput);
|
super(application, buildOutput);
|
||||||
this.exploded = () -> new File(buildOutput.getRootLocation(), "exploded");
|
this.exploded = () -> new File(buildOutput.getRootLocation(), "exploded");
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,8 +43,8 @@ class IdeApplicationLauncher extends AbstractApplicationLauncher {
|
||||||
|
|
||||||
private final File exploded;
|
private final File exploded;
|
||||||
|
|
||||||
IdeApplicationLauncher(ApplicationBuilder applicationBuilder, BuildOutput buildOutput) {
|
IdeApplicationLauncher(Application application, BuildOutput buildOutput) {
|
||||||
super(applicationBuilder, buildOutput);
|
super(application, buildOutput);
|
||||||
this.exploded = new File(buildOutput.getRootLocation(), "the+ide application");
|
this.exploded = new File(buildOutput.getRootLocation(), "the+ide application");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +108,7 @@ class IdeApplicationLauncher extends AbstractApplicationLauncher {
|
||||||
|
|
||||||
private File explodedResourcesProject(File dependencies) throws IOException {
|
private File explodedResourcesProject(File dependencies) throws IOException {
|
||||||
File resourcesProject = new File(this.exploded, "resources-project/built/classes");
|
File resourcesProject = new File(this.exploded, "resources-project/built/classes");
|
||||||
File resourcesJar = new File(dependencies, "resources-1.0.jar");
|
File resourcesJar = new File(dependencies, "spring-boot-server-tests-app-resources.jar");
|
||||||
explodeArchive(resourcesJar, resourcesProject);
|
explodeArchive(resourcesJar, resourcesProject);
|
||||||
resourcesJar.delete();
|
resourcesJar.delete();
|
||||||
return resourcesProject;
|
return resourcesProject;
|
||||||
|
@ -132,7 +132,7 @@ class IdeApplicationLauncher extends AbstractApplicationLauncher {
|
||||||
|
|
||||||
private List<String> getLibPaths(File archive) {
|
private List<String> getLibPaths(File archive) {
|
||||||
return (archive.getName().endsWith(".jar") ? Collections.singletonList("BOOT-INF/lib")
|
return (archive.getName().endsWith(".jar") ? Collections.singletonList("BOOT-INF/lib")
|
||||||
: Arrays.asList("WEB-INF/lib", "WEB-INF/lib-provided"));
|
: Arrays.asList("WEB-INF/lib"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void explodeArchive(File archive, File destination) throws IOException {
|
private void explodeArchive(File archive, File destination) throws IOException {
|
|
@ -30,8 +30,8 @@ import org.springframework.boot.testsupport.BuildOutput;
|
||||||
*/
|
*/
|
||||||
class PackagedApplicationLauncher extends AbstractApplicationLauncher {
|
class PackagedApplicationLauncher extends AbstractApplicationLauncher {
|
||||||
|
|
||||||
PackagedApplicationLauncher(ApplicationBuilder applicationBuilder, BuildOutput buildOutput) {
|
PackagedApplicationLauncher(Application application, BuildOutput buildOutput) {
|
||||||
super(applicationBuilder, buildOutput);
|
super(application, buildOutput);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
|
@ -1,184 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2012-2021 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
|
|
||||||
*
|
|
||||||
* https://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.context.embedded;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.FileReader;
|
|
||||||
import java.io.FileWriter;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.jar.JarOutputStream;
|
|
||||||
import java.util.zip.ZipEntry;
|
|
||||||
|
|
||||||
import com.samskivert.mustache.Mustache;
|
|
||||||
import org.apache.maven.shared.invoker.DefaultInvocationRequest;
|
|
||||||
import org.apache.maven.shared.invoker.DefaultInvoker;
|
|
||||||
import org.apache.maven.shared.invoker.InvocationRequest;
|
|
||||||
import org.apache.maven.shared.invoker.InvocationResult;
|
|
||||||
import org.apache.maven.shared.invoker.MavenInvocationException;
|
|
||||||
|
|
||||||
import org.springframework.util.FileCopyUtils;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Builds a Spring Boot application using Maven.
|
|
||||||
*
|
|
||||||
* @author Andy Wilkinson
|
|
||||||
*/
|
|
||||||
class ApplicationBuilder {
|
|
||||||
|
|
||||||
private final Path temp;
|
|
||||||
|
|
||||||
private final String packaging;
|
|
||||||
|
|
||||||
private final String container;
|
|
||||||
|
|
||||||
ApplicationBuilder(Path temp, String packaging, String container) {
|
|
||||||
this.temp = temp;
|
|
||||||
this.packaging = packaging;
|
|
||||||
this.container = container;
|
|
||||||
}
|
|
||||||
|
|
||||||
File buildApplication() throws Exception {
|
|
||||||
File containerDirectory = new File(this.temp.toFile(), this.container);
|
|
||||||
if (containerDirectory.exists()) {
|
|
||||||
return new File(containerDirectory, "app/target/app-0.0.1." + this.packaging);
|
|
||||||
}
|
|
||||||
return doBuildApplication(containerDirectory);
|
|
||||||
}
|
|
||||||
|
|
||||||
String getPackaging() {
|
|
||||||
return this.packaging;
|
|
||||||
}
|
|
||||||
|
|
||||||
String getContainer() {
|
|
||||||
return this.container;
|
|
||||||
}
|
|
||||||
|
|
||||||
private File doBuildApplication(File containerDirectory) throws IOException, MavenInvocationException {
|
|
||||||
File resourcesJar = createResourcesJar();
|
|
||||||
File appDirectory = new File(containerDirectory, "app");
|
|
||||||
appDirectory.mkdirs();
|
|
||||||
File settingsXml = writeSettingsXml(appDirectory);
|
|
||||||
writePom(appDirectory, resourcesJar);
|
|
||||||
copyApplicationSource(appDirectory);
|
|
||||||
packageApplication(appDirectory, settingsXml);
|
|
||||||
return new File(appDirectory, "target/app-0.0.1." + this.packaging);
|
|
||||||
}
|
|
||||||
|
|
||||||
private File createResourcesJar() throws IOException {
|
|
||||||
File resourcesJar = new File(this.temp.toFile(), "resources.jar");
|
|
||||||
if (resourcesJar.exists()) {
|
|
||||||
return resourcesJar;
|
|
||||||
}
|
|
||||||
try (JarOutputStream resourcesJarStream = new JarOutputStream(new FileOutputStream(resourcesJar))) {
|
|
||||||
resourcesJarStream.putNextEntry(new ZipEntry("META-INF/resources/"));
|
|
||||||
resourcesJarStream.closeEntry();
|
|
||||||
resourcesJarStream.putNextEntry(new ZipEntry("META-INF/resources/nested-meta-inf-resource.txt"));
|
|
||||||
resourcesJarStream.write("nested".getBytes());
|
|
||||||
resourcesJarStream.closeEntry();
|
|
||||||
if (!isWindows()) {
|
|
||||||
resourcesJarStream.putNextEntry(
|
|
||||||
new ZipEntry("META-INF/resources/nested-reserved-!#$%&()*+,:=?@[]-meta-inf-resource.txt"));
|
|
||||||
resourcesJarStream.write("encoded-name".getBytes());
|
|
||||||
resourcesJarStream.closeEntry();
|
|
||||||
}
|
|
||||||
return resourcesJar;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void writePom(File appDirectory, File resourcesJar) throws IOException {
|
|
||||||
Map<String, Object> context = new HashMap<>();
|
|
||||||
context.put("packaging", this.packaging);
|
|
||||||
context.put("container", this.container);
|
|
||||||
context.put("bootVersion", Versions.getBootVersion());
|
|
||||||
context.put("resourcesJarPath", resourcesJar.getAbsolutePath());
|
|
||||||
try (FileWriter out = new FileWriter(new File(appDirectory, "pom.xml"));
|
|
||||||
FileReader templateReader = new FileReader("src/test/resources/pom-template.xml")) {
|
|
||||||
Mustache.compiler().escapeHTML(false).compile(templateReader).execute(context, out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private File writeSettingsXml(File appDirectory) throws IOException {
|
|
||||||
Map<String, Object> context = new HashMap<>();
|
|
||||||
context.put("repository", new File("build/test-repository").toURI().toURL());
|
|
||||||
context.put("localRepository", new File("build/local-m2-repository").getAbsolutePath());
|
|
||||||
File settingsXml = new File(appDirectory, "settings.xml");
|
|
||||||
try (FileWriter out = new FileWriter(settingsXml);
|
|
||||||
FileReader templateReader = new FileReader("src/test/resources/settings-template.xml")) {
|
|
||||||
Mustache.compiler().escapeHTML(false).compile(templateReader).execute(context, out);
|
|
||||||
}
|
|
||||||
return settingsXml;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void copyApplicationSource(File appDirectory) throws IOException {
|
|
||||||
File examplePackage = new File(appDirectory, "src/main/java/com/example");
|
|
||||||
examplePackage.mkdirs();
|
|
||||||
FileCopyUtils.copy(new File("src/test/java/com/example/ResourceHandlingApplication.java"),
|
|
||||||
new File(examplePackage, "ResourceHandlingApplication.java"));
|
|
||||||
// To allow aliased resources on Concourse Windows CI (See gh-15553) to be served
|
|
||||||
// as static resources.
|
|
||||||
if (this.container.equals("jetty")) {
|
|
||||||
FileCopyUtils.copy(new File("src/test/java/com/example/JettyServerCustomizerConfig.java"),
|
|
||||||
new File(examplePackage, "JettyServerCustomizerConfig.java"));
|
|
||||||
}
|
|
||||||
if ("war".equals(this.packaging)) {
|
|
||||||
File srcMainWebapp = new File(appDirectory, "src/main/webapp");
|
|
||||||
srcMainWebapp.mkdirs();
|
|
||||||
FileCopyUtils.copy("webapp resource", new FileWriter(new File(srcMainWebapp, "webapp-resource.txt")));
|
|
||||||
}
|
|
||||||
copyAutoConfigurationFiles(appDirectory);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void copyAutoConfigurationFiles(File appDirectory) throws IOException {
|
|
||||||
File autoConfigPackage = new File(appDirectory, "src/main/java/com/autoconfig");
|
|
||||||
autoConfigPackage.mkdirs();
|
|
||||||
FileCopyUtils.copy(new File("src/test/java/com/autoconfig/ExampleAutoConfiguration.java"),
|
|
||||||
new File(autoConfigPackage, "ExampleAutoConfiguration.java"));
|
|
||||||
File srcMainResources = new File(appDirectory, "src/main/resources");
|
|
||||||
srcMainResources.mkdirs();
|
|
||||||
File metaInf = new File(srcMainResources, "META-INF");
|
|
||||||
metaInf.mkdirs();
|
|
||||||
FileCopyUtils.copy(new File("src/test/resources/META-INF/spring.factories"),
|
|
||||||
new File(metaInf, "spring.factories"));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void packageApplication(File appDirectory, File settingsXml) throws MavenInvocationException {
|
|
||||||
InvocationRequest invocation = new DefaultInvocationRequest();
|
|
||||||
invocation.setBaseDirectory(appDirectory);
|
|
||||||
invocation.setGoals(Collections.singletonList("package"));
|
|
||||||
if (settingsXml != null) {
|
|
||||||
invocation.setUserSettingsFile(settingsXml);
|
|
||||||
}
|
|
||||||
invocation.setUpdateSnapshots(true);
|
|
||||||
DefaultInvoker invoker = new DefaultInvoker();
|
|
||||||
invoker.setMavenHome(new File("build/maven-binaries/apache-maven-3.6.2"));
|
|
||||||
InvocationResult execute = invoker.execute(invocation);
|
|
||||||
assertThat(execute.getExitCode()).isEqualTo(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isWindows() {
|
|
||||||
return File.separatorChar == '\\';
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,87 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
<parent>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter-parent</artifactId>
|
|
||||||
<version>{{bootVersion}}</version>
|
|
||||||
<relativePath />
|
|
||||||
</parent>
|
|
||||||
<groupId>com.example</groupId>
|
|
||||||
<artifactId>app</artifactId>
|
|
||||||
<version>0.0.1</version>
|
|
||||||
<packaging>{{packaging}}</packaging>
|
|
||||||
<properties>
|
|
||||||
<resourcesJarPath>{{resourcesJarPath}}</resourcesJarPath>
|
|
||||||
</properties>
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter-{{container}}</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework</groupId>
|
|
||||||
<artifactId>spring-web</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>javax.servlet</groupId>
|
|
||||||
<artifactId>javax.servlet-api</artifactId>
|
|
||||||
<scope>provided</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.example</groupId>
|
|
||||||
<artifactId>resources</artifactId>
|
|
||||||
<version>1.0</version>
|
|
||||||
<scope>system</scope>
|
|
||||||
<systemPath>${resourcesJarPath}</systemPath>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
|
||||||
<configuration>
|
|
||||||
<includeSystemScope>true</includeSystemScope>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
<repositories>
|
|
||||||
<repository>
|
|
||||||
<id>spring-snapshots</id>
|
|
||||||
<url>https://repo.spring.io/snapshot</url>
|
|
||||||
<snapshots>
|
|
||||||
<enabled>true</enabled>
|
|
||||||
</snapshots>
|
|
||||||
</repository>
|
|
||||||
<repository>
|
|
||||||
<id>spring-milestones</id>
|
|
||||||
<url>https://repo.spring.io/milestone</url>
|
|
||||||
<snapshots>
|
|
||||||
<enabled>false</enabled>
|
|
||||||
</snapshots>
|
|
||||||
</repository>
|
|
||||||
</repositories>
|
|
||||||
<pluginRepositories>
|
|
||||||
<pluginRepository>
|
|
||||||
<id>spring-snapshots</id>
|
|
||||||
<url>https://repo.spring.io/snapshot</url>
|
|
||||||
<snapshots>
|
|
||||||
<enabled>true</enabled>
|
|
||||||
</snapshots>
|
|
||||||
</pluginRepository>
|
|
||||||
<pluginRepository>
|
|
||||||
<id>spring-milestones</id>
|
|
||||||
<url>https://repo.spring.io/milestone</url>
|
|
||||||
<snapshots>
|
|
||||||
<enabled>false</enabled>
|
|
||||||
</snapshots>
|
|
||||||
</pluginRepository>
|
|
||||||
</pluginRepositories>
|
|
||||||
</project>
|
|
|
@ -1,31 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 https://maven.apache.org/xsd/settings-1.0.0.xsd">
|
|
||||||
<localRepository>{{localRepository}}</localRepository>
|
|
||||||
<profiles>
|
|
||||||
<profile>
|
|
||||||
<id>repository</id>
|
|
||||||
<activation>
|
|
||||||
<activeByDefault>true</activeByDefault>
|
|
||||||
</activation>
|
|
||||||
<repositories>
|
|
||||||
<repository>
|
|
||||||
<id>repository</id>
|
|
||||||
<url>{{repository}}</url>
|
|
||||||
<snapshots>
|
|
||||||
<enabled>true</enabled>
|
|
||||||
</snapshots>
|
|
||||||
</repository>
|
|
||||||
</repositories>
|
|
||||||
<pluginRepositories>
|
|
||||||
<pluginRepository>
|
|
||||||
<id>repository</id>
|
|
||||||
<url>{{repository}}</url>
|
|
||||||
<snapshots>
|
|
||||||
<enabled>true</enabled>
|
|
||||||
</snapshots>
|
|
||||||
</pluginRepository>
|
|
||||||
</pluginRepositories>
|
|
||||||
</profile>
|
|
||||||
</profiles>
|
|
||||||
</settings>
|
|
Loading…
Reference in New Issue