Port the build to Gradle

Closes gh-19609
Closes gh-19608
This commit is contained in:
Andy Wilkinson
2020-01-10 13:48:43 +00:00
parent abe95fa8a7
commit ce99db1902
974 changed files with 17108 additions and 26596 deletions

View File

@@ -1,35 +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-tests</artifactId>
<version>${revision}</version>
</parent>
<artifactId>spring-boot-integration-tests</artifactId>
<packaging>pom</packaging>
<name>Spring Boot Integration Tests</name>
<description>Spring Boot Integration Tests</description>
<properties>
<main.basedir>${basedir}/../..</main.basedir>
<java.version>1.8</java.version>
</properties>
<modules>
<module>spring-boot-configuration-processor-tests</module>
<module>spring-boot-devtools-tests</module>
<module>spring-boot-server-tests</module>
<module>spring-boot-launch-script-tests</module>
</modules>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-install-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,17 @@
plugins {
id 'java'
id 'org.springframework.boot.conventions'
}
description = "Spring Boot Configuration Processor Tests"
dependencies {
annotationProcessor project(':spring-boot-project:spring-boot-tools:spring-boot-configuration-processor')
implementation enforcedPlatform(project(':spring-boot-project:spring-boot-dependencies'))
implementation project(':spring-boot-project:spring-boot')
implementation 'jakarta.validation:jakarta.validation-api'
testImplementation project(':spring-boot-project:spring-boot-starters:spring-boot-starter-test')
testImplementation project(':spring-boot-project:spring-boot-tools:spring-boot-configuration-metadata')
}

View File

@@ -1,40 +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-integration-tests</artifactId>
<version>${revision}</version>
</parent>
<artifactId>spring-boot-configuration-processor-tests</artifactId>
<name>Spring Boot Configuration Processor Tests</name>
<description>${project.name}</description>
<properties>
<main.basedir>${basedir}/../../..</main.basedir>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-metadata</artifactId>
</dependency>
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -1,67 +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-integration-tests</artifactId>
<version>${revision}</version>
</parent>
<artifactId>spring-boot-devtools-tests</artifactId>
<name>Spring Boot DevTools Tests</name>
<description>${project.name}</description>
<properties>
<main.basedir>${basedir}/../../..</main.basedir>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test-support</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>process-test-resources</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<includeScope>runtime</includeScope>
<outputDirectory>${project.build.directory}/dependencies</outputDirectory>
<overWriteSnapshots>true</overWriteSnapshots>
<overWriteIfNewer>true</overWriteIfNewer>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -1,30 +0,0 @@
/*
* Copyright 2012-2019 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 com.example;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ControllerOne {
@RequestMapping("/one")
public String one() {
return "one";
}
}

View File

@@ -1,31 +0,0 @@
/*
* Copyright 2012-2019 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 com.example;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.context.WebServerPortFileWriter;
@SpringBootApplication
public class DevToolsTestApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(DevToolsTestApplication.class).listeners(new WebServerPortFileWriter(args[0]))
.run(args);
}
}

View File

@@ -1,56 +0,0 @@
/*
* Copyright 2012-2019 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.devtools.tests;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.springframework.util.FileSystemUtils;
/**
* Base class for all {@link ApplicationLauncher} implementations.
*
* @author Andy Wilkinson
*/
abstract class AbstractApplicationLauncher implements ApplicationLauncher {
private final Directories directories;
AbstractApplicationLauncher(Directories directories) {
this.directories = directories;
}
protected final void copyApplicationTo(File location) throws IOException {
FileSystemUtils.deleteRecursively(location);
location.mkdirs();
FileSystemUtils.copyRecursively(new File(this.directories.getTestClassesDirectory(), "com"),
new File(location, "com"));
}
protected final List<String> getDependencyJarPaths() {
return Stream.of(this.directories.getDependenciesDirectory().listFiles()).map(File::getAbsolutePath)
.collect(Collectors.toList());
}
protected final Directories getDirectories() {
return this.directories;
}
}

View File

@@ -1,114 +0,0 @@
/*
* Copyright 2012-2019 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.devtools.tests;
import java.io.File;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.description.annotation.AnnotationDescription;
import net.bytebuddy.description.modifier.Visibility;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.FixedValue;
import org.awaitility.Awaitility;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.junit.jupiter.api.io.TempDir;
import org.springframework.boot.testsupport.BuildOutput;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* Base class for DevTools integration tests.
*
* @author Andy Wilkinson
*/
abstract class AbstractDevToolsIntegrationTests {
protected static final BuildOutput buildOutput = new BuildOutput(AbstractDevToolsIntegrationTests.class);
protected final File serverPortFile = new File(buildOutput.getRootLocation(), "server.port");
@RegisterExtension
protected final JvmLauncher javaLauncher = new JvmLauncher();
@TempDir
protected static File temp;
protected LaunchedApplication launchedApplication;
protected void launchApplication(ApplicationLauncher applicationLauncher, String... args) throws Exception {
this.serverPortFile.delete();
this.launchedApplication = applicationLauncher.launchApplication(this.javaLauncher, this.serverPortFile, args);
}
@AfterEach
void stopApplication() throws InterruptedException {
this.launchedApplication.stop();
}
protected int awaitServerPort() throws Exception {
int port = Awaitility.waitAtMost(Duration.ofSeconds(30))
.until(() -> new ApplicationState(this.serverPortFile, this.launchedApplication),
ApplicationState::hasServerPort)
.getServerPort();
this.serverPortFile.delete();
System.out.println("Got port " + port);
this.launchedApplication.restartRemote(port);
Thread.sleep(1000);
return port;
}
protected ControllerBuilder controller(String name) {
return new ControllerBuilder(name, this.launchedApplication.getClassesDirectory());
}
protected static final class ControllerBuilder {
private final List<String> mappings = new ArrayList<>();
private final String name;
private final File classesDirectory;
protected ControllerBuilder(String name, File classesDirectory) {
this.name = name;
this.classesDirectory = classesDirectory;
}
protected ControllerBuilder withRequestMapping(String mapping) {
this.mappings.add(mapping);
return this;
}
protected void build() throws Exception {
DynamicType.Builder<Object> builder = new ByteBuddy().subclass(Object.class).name(this.name)
.annotateType(AnnotationDescription.Builder.ofType(RestController.class).build());
for (String mapping : this.mappings) {
builder = builder.defineMethod(mapping, String.class, Visibility.PUBLIC)
.intercept(FixedValue.value(mapping)).annotateMethod(AnnotationDescription.Builder
.ofType(RequestMapping.class).defineArray("value", mapping).build());
}
builder.make().saveIn(this.classesDirectory);
}
}
}

View File

@@ -1,34 +0,0 @@
/*
* Copyright 2012-2019 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.devtools.tests;
import java.io.File;
/**
* Launches an application with DevTools.
*
* @author Andy Wilkinson
* @author Madhura Bhave
*/
public interface ApplicationLauncher {
LaunchedApplication launchApplication(JvmLauncher javaLauncher, File serverPortFile) throws Exception;
LaunchedApplication launchApplication(JvmLauncher jvmLauncher, File serverPortFile, String... additionalArgs)
throws Exception;
}

View File

@@ -1,63 +0,0 @@
/*
* Copyright 2012-2019 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.devtools.tests;
import java.io.File;
import org.springframework.boot.devtools.tests.JvmLauncher.LaunchedJvm;
/**
* State of an application.
*
* @author Andy Wilkinson
*/
final class ApplicationState {
private final Integer serverPort;
private final FileContents out;
private final FileContents err;
ApplicationState(File serverPortFile, LaunchedJvm jvm) {
this(serverPortFile, jvm.getStandardOut(), jvm.getStandardError());
}
ApplicationState(File serverPortFile, LaunchedApplication application) {
this(serverPortFile, application.getStandardOut(), application.getStandardError());
}
private ApplicationState(File serverPortFile, File out, File err) {
this.serverPort = new FileContents(serverPortFile).get(Integer::parseInt);
this.out = new FileContents(out);
this.err = new FileContents(err);
}
boolean hasServerPort() {
return this.serverPort != null;
}
int getServerPort() {
return this.serverPort;
}
@Override
public String toString() {
return String.format("Application output:%n%s%n%s", this.out, this.err);
}
}

View File

@@ -1,162 +0,0 @@
/*
* Copyright 2012-2019 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.devtools.tests;
import java.io.File;
import java.io.IOException;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.HttpStatus;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration tests for DevTools.
*
* @author Andy Wilkinson
*/
class DevToolsIntegrationTests extends AbstractDevToolsIntegrationTests {
@ParameterizedTest(name = "{0}")
@MethodSource("parameters")
void addARequestMappingToAnExistingController(ApplicationLauncher applicationLauncher) throws Exception {
launchApplication(applicationLauncher);
TestRestTemplate template = new TestRestTemplate();
String urlBase = "http://localhost:" + awaitServerPort();
assertThat(template.getForObject(urlBase + "/one", String.class)).isEqualTo("one");
assertThat(template.getForEntity(urlBase + "/two", String.class).getStatusCode())
.isEqualTo(HttpStatus.NOT_FOUND);
controller("com.example.ControllerOne").withRequestMapping("one").withRequestMapping("two").build();
urlBase = "http://localhost:" + awaitServerPort();
assertThat(template.getForObject(urlBase + "/one", String.class)).isEqualTo("one");
assertThat(template.getForObject(urlBase + "/two", String.class)).isEqualTo("two");
}
@ParameterizedTest(name = "{0}")
@MethodSource("parameters")
void removeARequestMappingFromAnExistingController(ApplicationLauncher applicationLauncher) throws Exception {
launchApplication(applicationLauncher);
TestRestTemplate template = new TestRestTemplate();
String urlBase = "http://localhost:" + awaitServerPort();
assertThat(template.getForObject(urlBase + "/one", String.class)).isEqualTo("one");
controller("com.example.ControllerOne").build();
urlBase = "http://localhost:" + awaitServerPort();
assertThat(template.getForEntity(urlBase + "/one", String.class).getStatusCode())
.isEqualTo(HttpStatus.NOT_FOUND);
}
@ParameterizedTest(name = "{0}")
@MethodSource("parameters")
void createAController(ApplicationLauncher applicationLauncher) throws Exception {
launchApplication(applicationLauncher);
TestRestTemplate template = new TestRestTemplate();
String urlBase = "http://localhost:" + awaitServerPort();
assertThat(template.getForObject(urlBase + "/one", String.class)).isEqualTo("one");
assertThat(template.getForEntity(urlBase + "/two", String.class).getStatusCode())
.isEqualTo(HttpStatus.NOT_FOUND);
controller("com.example.ControllerTwo").withRequestMapping("two").build();
urlBase = "http://localhost:" + awaitServerPort();
assertThat(template.getForObject(urlBase + "/one", String.class)).isEqualTo("one");
assertThat(template.getForObject(urlBase + "/two", String.class)).isEqualTo("two");
}
@ParameterizedTest(name = "{0}")
@MethodSource("parameters")
void createAControllerAndThenAddARequestMapping(ApplicationLauncher applicationLauncher) throws Exception {
launchApplication(applicationLauncher);
TestRestTemplate template = new TestRestTemplate();
String urlBase = "http://localhost:" + awaitServerPort();
assertThat(template.getForObject(urlBase + "/one", String.class)).isEqualTo("one");
assertThat(template.getForEntity(urlBase + "/two", String.class).getStatusCode())
.isEqualTo(HttpStatus.NOT_FOUND);
controller("com.example.ControllerTwo").withRequestMapping("two").build();
urlBase = "http://localhost:" + awaitServerPort();
assertThat(template.getForObject(urlBase + "/one", String.class)).isEqualTo("one");
assertThat(template.getForObject(urlBase + "/two", String.class)).isEqualTo("two");
controller("com.example.ControllerTwo").withRequestMapping("two").withRequestMapping("three").build();
urlBase = "http://localhost:" + awaitServerPort();
assertThat(template.getForObject(urlBase + "/three", String.class)).isEqualTo("three");
}
@ParameterizedTest(name = "{0}")
@MethodSource("parameters")
void createAControllerAndThenAddARequestMappingToAnExistingController(ApplicationLauncher applicationLauncher)
throws Exception {
launchApplication(applicationLauncher);
TestRestTemplate template = new TestRestTemplate();
String urlBase = "http://localhost:" + awaitServerPort();
assertThat(template.getForObject(urlBase + "/one", String.class)).isEqualTo("one");
assertThat(template.getForEntity(urlBase + "/two", String.class).getStatusCode())
.isEqualTo(HttpStatus.NOT_FOUND);
controller("com.example.ControllerTwo").withRequestMapping("two").build();
urlBase = "http://localhost:" + awaitServerPort();
assertThat(template.getForObject(urlBase + "/one", String.class)).isEqualTo("one");
assertThat(template.getForObject(urlBase + "/two", String.class)).isEqualTo("two");
controller("com.example.ControllerOne").withRequestMapping("one").withRequestMapping("three").build();
urlBase = "http://localhost:" + awaitServerPort();
assertThat(template.getForObject(urlBase + "/one", String.class)).isEqualTo("one");
assertThat(template.getForObject(urlBase + "/two", String.class)).isEqualTo("two");
assertThat(template.getForObject(urlBase + "/three", String.class)).isEqualTo("three");
}
@ParameterizedTest(name = "{0}")
@MethodSource("parameters")
void deleteAController(ApplicationLauncher applicationLauncher) throws Exception {
launchApplication(applicationLauncher);
TestRestTemplate template = new TestRestTemplate();
String urlBase = "http://localhost:" + awaitServerPort();
assertThat(template.getForObject(urlBase + "/one", String.class)).isEqualTo("one");
assertThat(new File(this.launchedApplication.getClassesDirectory(), "com/example/ControllerOne.class").delete())
.isTrue();
urlBase = "http://localhost:" + awaitServerPort();
assertThat(template.getForEntity(urlBase + "/one", String.class).getStatusCode())
.isEqualTo(HttpStatus.NOT_FOUND);
}
@ParameterizedTest(name = "{0}")
@MethodSource("parameters")
void createAControllerAndThenDeleteIt(ApplicationLauncher applicationLauncher) throws Exception {
launchApplication(applicationLauncher);
TestRestTemplate template = new TestRestTemplate();
String urlBase = "http://localhost:" + awaitServerPort();
assertThat(template.getForObject(urlBase + "/one", String.class)).isEqualTo("one");
assertThat(template.getForEntity(urlBase + "/two", String.class).getStatusCode())
.isEqualTo(HttpStatus.NOT_FOUND);
controller("com.example.ControllerTwo").withRequestMapping("two").build();
urlBase = "http://localhost:" + awaitServerPort();
assertThat(template.getForObject(urlBase + "/one", String.class)).isEqualTo("one");
assertThat(template.getForObject(urlBase + "/two", String.class)).isEqualTo("two");
assertThat(new File(this.launchedApplication.getClassesDirectory(), "com/example/ControllerTwo.class").delete())
.isTrue();
urlBase = "http://localhost:" + awaitServerPort();
assertThat(template.getForEntity(urlBase + "/two", String.class).getStatusCode())
.isEqualTo(HttpStatus.NOT_FOUND);
}
static Object[] parameters() throws IOException {
Directories directories = new Directories(buildOutput, temp);
return new Object[] { new Object[] { new LocalApplicationLauncher(directories) },
new Object[] { new ExplodedRemoteApplicationLauncher(directories) },
new Object[] { new JarFileRemoteApplicationLauncher(directories) } };
}
}

View File

@@ -1,60 +0,0 @@
/*
* Copyright 2012-2019 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.devtools.tests;
import java.io.IOException;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.HttpStatus;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration tests for DevTools with lazy initialization enabled.
*
* @author Madhura Bhave
*/
class DevToolsWithLazyInitializationIntegrationTests extends AbstractDevToolsIntegrationTests {
@ParameterizedTest(name = "{0}")
@MethodSource("parameters")
void addARequestMappingToAnExistingControllerWhenLazyInit(ApplicationLauncher applicationLauncher)
throws Exception {
launchApplication(applicationLauncher, "--spring.main.lazy-initialization=true");
TestRestTemplate template = new TestRestTemplate();
String urlBase = "http://localhost:" + awaitServerPort();
assertThat(template.getForObject(urlBase + "/one", String.class)).isEqualTo("one");
assertThat(template.getForEntity(urlBase + "/two", String.class).getStatusCode())
.isEqualTo(HttpStatus.NOT_FOUND);
controller("com.example.ControllerOne").withRequestMapping("one").withRequestMapping("two").build();
urlBase = "http://localhost:" + awaitServerPort();
assertThat(template.getForObject(urlBase + "/one", String.class)).isEqualTo("one");
assertThat(template.getForObject(urlBase + "/two", String.class)).isEqualTo("two");
}
static Object[] parameters() throws IOException {
Directories directories = new Directories(buildOutput, temp);
return new Object[] { new Object[] { new LocalApplicationLauncher(directories) },
new Object[] { new ExplodedRemoteApplicationLauncher(directories) },
new Object[] { new JarFileRemoteApplicationLauncher(directories) } };
}
}

View File

@@ -1,55 +0,0 @@
/*
* Copyright 2012-2019 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.devtools.tests;
import java.io.File;
import org.springframework.boot.testsupport.BuildOutput;
/**
* Various directories used by the {@link ApplicationLauncher ApplicationLaunchers}.
*
* @author Andy Wilkinson
*/
class Directories {
private final BuildOutput buildOutput;
private final File temp;
Directories(BuildOutput buildOutput, File temp) {
this.buildOutput = buildOutput;
this.temp = temp;
}
File getTestClassesDirectory() {
return this.buildOutput.getTestClassesLocation();
}
File getRemoteAppDirectory() {
return new File(this.temp, "remote");
}
File getDependenciesDirectory() {
return new File(this.buildOutput.getRootLocation(), "dependencies");
}
File getAppDirectory() {
return new File(this.temp, "app");
}
}

View File

@@ -1,52 +0,0 @@
/*
* Copyright 2012-2019 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.devtools.tests;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import org.springframework.util.StringUtils;
/**
* {@link ApplicationLauncher} that launches a remote application with its classes
* available directly on the file system.
*
* @author Andy Wilkinson
*/
public class ExplodedRemoteApplicationLauncher extends RemoteApplicationLauncher {
public ExplodedRemoteApplicationLauncher(Directories directories) {
super(directories);
}
@Override
protected String createApplicationClassPath() throws Exception {
File appDirectory = getDirectories().getAppDirectory();
copyApplicationTo(appDirectory);
List<String> entries = new ArrayList<>();
entries.add(appDirectory.getAbsolutePath());
entries.addAll(getDependencyJarPaths());
return StringUtils.collectionToDelimitedString(entries, File.pathSeparator);
}
@Override
public String toString() {
return "exploded remote";
}
}

View File

@@ -1,60 +0,0 @@
/*
* Copyright 2012-2019 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.devtools.tests;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.function.Function;
import org.springframework.util.FileCopyUtils;
/**
* Provides access to the contents of a file.
*
* @author Andy Wilkinson
*/
class FileContents {
private final File file;
FileContents(File file) {
this.file = file;
}
String get() {
return get(Function.identity());
}
<T> T get(Function<String, T> transformer) {
if ((!this.file.exists()) || this.file.length() == 0) {
return null;
}
try {
return transformer.apply(FileCopyUtils.copyToString(new FileReader(this.file)));
}
catch (IOException ex) {
throw new RuntimeException(ex);
}
}
@Override
public String toString() {
return get();
}
}

View File

@@ -1,84 +0,0 @@
/*
* Copyright 2012-2019 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.devtools.tests;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.jar.Attributes;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import java.util.zip.ZipEntry;
import org.springframework.util.StreamUtils;
import org.springframework.util.StringUtils;
/**
* {@link ApplicationLauncher} that launches a remote application with its classes in a
* jar file.
*
* @author Andy Wilkinson
*/
public class JarFileRemoteApplicationLauncher extends RemoteApplicationLauncher {
public JarFileRemoteApplicationLauncher(Directories directories) {
super(directories);
}
@Override
protected String createApplicationClassPath() throws Exception {
File appDirectory = getDirectories().getAppDirectory();
copyApplicationTo(appDirectory);
Manifest manifest = new Manifest();
manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
File appJar = new File(appDirectory, "app.jar");
JarOutputStream output = new JarOutputStream(new FileOutputStream(appJar), manifest);
addToJar(output, appDirectory, appDirectory);
output.close();
List<String> entries = new ArrayList<>();
entries.add(appJar.getAbsolutePath());
entries.addAll(getDependencyJarPaths());
String classpath = StringUtils.collectionToDelimitedString(entries, File.pathSeparator);
return classpath;
}
private void addToJar(JarOutputStream output, File root, File current) throws IOException {
for (File file : current.listFiles()) {
if (file.isDirectory()) {
addToJar(output, root, file);
}
output.putNextEntry(new ZipEntry(
file.getAbsolutePath().substring(root.getAbsolutePath().length() + 1).replace("\\", "/")
+ (file.isDirectory() ? "/" : "")));
if (file.isFile()) {
try (FileInputStream input = new FileInputStream(file)) {
StreamUtils.copy(input, output);
}
}
output.closeEntry();
}
}
@Override
public String toString() {
return "jar file remote";
}
}

View File

@@ -1,93 +0,0 @@
/*
* Copyright 2012-2019 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.devtools.tests;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;
import org.junit.jupiter.api.extension.BeforeTestExecutionCallback;
import org.junit.jupiter.api.extension.Extension;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.springframework.boot.testsupport.BuildOutput;
import org.springframework.util.StringUtils;
/**
* {@link Extension} that launches a JVM and redirects its output to a test
* method-specific location.
*
* @author Andy Wilkinson
*/
class JvmLauncher implements BeforeTestExecutionCallback {
private static final Pattern NON_ALPHABET_PATTERN = Pattern.compile("[^A-Za-z]+");
private final BuildOutput buildOutput = new BuildOutput(getClass());
private File outputDirectory;
@Override
public void beforeTestExecution(ExtensionContext context) throws Exception {
this.outputDirectory = new File(this.buildOutput.getRootLocation(),
"output/" + NON_ALPHABET_PATTERN.matcher(context.getRequiredTestMethod().getName()).replaceAll(""));
this.outputDirectory.mkdirs();
}
LaunchedJvm launch(String name, String classpath, String... args) throws IOException {
List<String> command = new ArrayList<>(
Arrays.asList(System.getProperty("java.home") + "/bin/java", "-cp", classpath));
command.addAll(Arrays.asList(args));
File standardOut = new File(this.outputDirectory, name + ".out");
File standardError = new File(this.outputDirectory, name + ".err");
Process process = new ProcessBuilder(StringUtils.toStringArray(command)).redirectError(standardError)
.redirectOutput(standardOut).start();
return new LaunchedJvm(process, standardOut, standardError);
}
static class LaunchedJvm {
private final Process process;
private final File standardOut;
private final File standardError;
LaunchedJvm(Process process, File standardOut, File standardError) {
this.process = process;
this.standardOut = standardOut;
this.standardError = standardError;
}
Process getProcess() {
return this.process;
}
File getStandardOut() {
return this.standardOut;
}
File getStandardError() {
return this.standardError;
}
}
}

View File

@@ -1,82 +0,0 @@
/*
* Copyright 2012-2019 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.devtools.tests;
import java.io.File;
import java.util.function.BiFunction;
/**
* An application launched by {@link ApplicationLauncher}.
*
* @author Andy Wilkinson
*/
class LaunchedApplication {
private final File classesDirectory;
private final File standardOut;
private final File standardError;
private final Process localProcess;
private Process remoteProcess;
private final BiFunction<Integer, File, Process> remoteProcessRestarter;
LaunchedApplication(File classesDirectory, File standardOut, File standardError, Process localProcess,
Process remoteProcess, BiFunction<Integer, File, Process> remoteProcessRestarter) {
this.classesDirectory = classesDirectory;
this.standardOut = standardOut;
this.standardError = standardError;
this.localProcess = localProcess;
this.remoteProcess = remoteProcess;
this.remoteProcessRestarter = remoteProcessRestarter;
}
void restartRemote(int port) throws InterruptedException {
if (this.remoteProcessRestarter != null) {
stop(this.remoteProcess);
this.remoteProcess = this.remoteProcessRestarter.apply(port, this.classesDirectory);
}
}
void stop() throws InterruptedException {
stop(this.localProcess);
stop(this.remoteProcess);
}
private void stop(Process process) throws InterruptedException {
if (process != null) {
process.destroy();
process.waitFor();
}
}
File getStandardOut() {
return this.standardOut;
}
File getStandardError() {
return this.standardError;
}
File getClassesDirectory() {
return this.classesDirectory;
}
}

View File

@@ -1,71 +0,0 @@
/*
* Copyright 2012-2019 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.devtools.tests;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.springframework.boot.devtools.tests.JvmLauncher.LaunchedJvm;
import org.springframework.util.StringUtils;
/**
* {@link ApplicationLauncher} that launches a local application with DevTools enabled.
*
* @author Andy Wilkinson
*/
public class LocalApplicationLauncher extends AbstractApplicationLauncher {
LocalApplicationLauncher(Directories directories) {
super(directories);
}
@Override
public LaunchedApplication launchApplication(JvmLauncher jvmLauncher, File serverPortFile) throws Exception {
LaunchedJvm jvm = jvmLauncher.launch("local", createApplicationClassPath(),
"com.example.DevToolsTestApplication", serverPortFile.getAbsolutePath(), "--server.port=0");
return new LaunchedApplication(getDirectories().getAppDirectory(), jvm.getStandardOut(), jvm.getStandardError(),
jvm.getProcess(), null, null);
}
@Override
public LaunchedApplication launchApplication(JvmLauncher jvmLauncher, File serverPortFile, String... additionalArgs)
throws Exception {
List<String> args = new ArrayList<>(Arrays.asList("com.example.DevToolsTestApplication",
serverPortFile.getAbsolutePath(), "--server.port=0"));
args.addAll(Arrays.asList(additionalArgs));
LaunchedJvm jvm = jvmLauncher.launch("local", createApplicationClassPath(), args.toArray(new String[] {}));
return new LaunchedApplication(getDirectories().getAppDirectory(), jvm.getStandardOut(), jvm.getStandardError(),
jvm.getProcess(), null, null);
}
protected String createApplicationClassPath() throws Exception {
File appDirectory = getDirectories().getAppDirectory();
copyApplicationTo(appDirectory);
List<String> entries = new ArrayList<>();
entries.add(appDirectory.getAbsolutePath());
entries.addAll(getDependencyJarPaths());
return StringUtils.collectionToDelimitedString(entries, File.pathSeparator);
}
@Override
public String toString() {
return "local";
}
}

View File

@@ -1,114 +0,0 @@
/*
* Copyright 2012-2019 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.devtools.tests;
import java.io.File;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.BiFunction;
import org.awaitility.Awaitility;
import org.springframework.boot.devtools.RemoteSpringApplication;
import org.springframework.boot.devtools.tests.JvmLauncher.LaunchedJvm;
import org.springframework.util.StringUtils;
import static org.hamcrest.Matchers.containsString;
/**
* Base class for {@link ApplicationLauncher} implementations that use
* {@link RemoteSpringApplication}.
*
* @author Andy Wilkinson
*/
abstract class RemoteApplicationLauncher extends AbstractApplicationLauncher {
RemoteApplicationLauncher(Directories directories) {
super(directories);
}
@Override
public LaunchedApplication launchApplication(JvmLauncher javaLauncher, File serverPortFile) throws Exception {
LaunchedJvm applicationJvm = javaLauncher.launch("app", createApplicationClassPath(),
"com.example.DevToolsTestApplication", serverPortFile.getAbsolutePath(), "--server.port=0",
"--spring.devtools.remote.secret=secret");
int port = awaitServerPort(applicationJvm, serverPortFile);
BiFunction<Integer, File, Process> remoteRestarter = getRemoteRestarter(javaLauncher);
return new LaunchedApplication(getDirectories().getRemoteAppDirectory(), applicationJvm.getStandardOut(),
applicationJvm.getStandardError(), applicationJvm.getProcess(), remoteRestarter.apply(port, null),
remoteRestarter);
}
@Override
public LaunchedApplication launchApplication(JvmLauncher javaLauncher, File serverPortFile,
String... additionalArgs) throws Exception {
List<String> args = new ArrayList<>(Arrays.asList("com.example.DevToolsTestApplication",
serverPortFile.getAbsolutePath(), "--server.port=0", "--spring.devtools.remote.secret=secret"));
args.addAll(Arrays.asList(additionalArgs));
LaunchedJvm applicationJvm = javaLauncher.launch("app", createApplicationClassPath(),
args.toArray(new String[] {}));
int port = awaitServerPort(applicationJvm, serverPortFile);
BiFunction<Integer, File, Process> remoteRestarter = getRemoteRestarter(javaLauncher);
return new LaunchedApplication(getDirectories().getRemoteAppDirectory(), applicationJvm.getStandardOut(),
applicationJvm.getStandardError(), applicationJvm.getProcess(), remoteRestarter.apply(port, null),
remoteRestarter);
}
private BiFunction<Integer, File, Process> getRemoteRestarter(JvmLauncher javaLauncher) {
return (port, classesDirectory) -> {
try {
LaunchedJvm remoteSpringApplicationJvm = javaLauncher.launch("remote-spring-application",
createRemoteSpringApplicationClassPath(classesDirectory),
RemoteSpringApplication.class.getName(), "--spring.devtools.remote.secret=secret",
"http://localhost:" + port);
awaitRemoteSpringApplication(remoteSpringApplicationJvm.getStandardOut());
return remoteSpringApplicationJvm.getProcess();
}
catch (Exception ex) {
throw new IllegalStateException(ex);
}
};
}
protected abstract String createApplicationClassPath() throws Exception;
private String createRemoteSpringApplicationClassPath(File classesDirectory) throws Exception {
File remoteAppDirectory = getDirectories().getRemoteAppDirectory();
if (classesDirectory == null) {
copyApplicationTo(remoteAppDirectory);
}
List<String> entries = new ArrayList<>();
entries.add(remoteAppDirectory.getAbsolutePath());
entries.addAll(getDependencyJarPaths());
return StringUtils.collectionToDelimitedString(entries, File.pathSeparator);
}
private int awaitServerPort(LaunchedJvm jvm, File serverPortFile) throws Exception {
return Awaitility.waitAtMost(Duration.ofSeconds(30))
.until(() -> new ApplicationState(serverPortFile, jvm), ApplicationState::hasServerPort)
.getServerPort();
}
private void awaitRemoteSpringApplication(File standardOut) throws Exception {
FileContents contents = new FileContents(standardOut);
Awaitility.waitAtMost(Duration.ofSeconds(30)).until(contents::get,
containsString("Started RemoteSpringApplication"));
}
}

View File

@@ -0,0 +1,21 @@
plugins {
id 'java'
id 'org.springframework.boot'
}
apply plugin: 'io.spring.dependency-management'
repositories {
maven { url "file:$rootDir/../int-test-maven-repository"}
mavenCentral()
maven { url 'https://repo.spring.io/snapshot' }
maven { url 'https://repo.spring.io/milestone' }
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
}
bootJar {
launchScript()
}

View File

@@ -0,0 +1,15 @@
pluginManagement {
repositories {
maven { url "file:$rootDir/../int-test-maven-repository"}
mavenCentral()
maven { url 'https://repo.spring.io/snapshot' }
maven { url 'https://repo.spring.io/milestone' }
}
resolutionStrategy {
eachPlugin {
if (requested.id.id == 'org.springframework.boot') {
useModule "org.springframework.boot:spring-boot-gradle-plugin:${requested.version}"
}
}
}
}

View File

@@ -0,0 +1,45 @@
plugins {
id 'java'
id 'org.springframework.boot.conventions'
id 'org.springframework.boot.integration-test'
}
description = "Spring Boot Launch Script Integration Tests"
configurations {
app
}
dependencies {
app project(path: ':spring-boot-project:spring-boot-tools:spring-boot-gradle-plugin', configuration: 'mavenRepository')
app project(path: ':spring-boot-project:spring-boot-starters:spring-boot-starter-web', configuration: 'mavenRepository')
intTestImplementation enforcedPlatform(project(':spring-boot-project:spring-boot-parent'))
intTestImplementation project(':spring-boot-project:spring-boot-starters:spring-boot-starter-test')
intTestImplementation 'org.testcontainers:testcontainers'
}
task syncMavenRepository(type: Sync) {
from configurations.app
into "$buildDir/int-test-maven-repository"
}
task syncAppSource(type: Sync) {
from 'app'
into "$buildDir/app"
filter { line ->
line.replace("id 'org.springframework.boot'", "id 'org.springframework.boot' version '${project.version}'")
}
}
task buildApp(type: GradleBuild) {
dependsOn syncAppSource, syncMavenRepository
dir = "$buildDir/app"
startParameter.buildCacheEnabled = false
tasks = ['build']
}
intTest {
dependsOn buildApp
enabled = !JavaVersion.current().java9Compatible
}

View File

@@ -1,110 +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-integration-tests</artifactId>
<version>${revision}</version>
</parent>
<artifactId>spring-boot-launch-script-tests</artifactId>
<packaging>jar</packaging>
<name>Spring Boot Launch Script Integration Tests</name>
<description>Spring Boot Launch Script Integration Tests</description>
<properties>
<main.basedir>${basedir}/../../..</main.basedir>
<jersey.version>2.27</jersey.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
</exclusion>
<exclusion>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
</exclusion>
<exclusion>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>jakarta.ws.rs</groupId>
<artifactId>jakarta.ws.rs-api</artifactId>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
</exclusion>
<exclusion>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
</exclusion>
<exclusion>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<profiles>
<profile>
<id>docker</id>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<executable>true</executable>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@@ -42,7 +42,7 @@ import static org.hamcrest.Matchers.containsString;
* @author Andy Wilkinson
* @author Ali Shahbour
*/
class SysVinitLaunchScriptIT {
class SysVinitLaunchScriptIntegrationTests {
private static final char ESC = 27;
@@ -279,7 +279,7 @@ class SysVinitLaunchScriptIT {
static List<Object[]> parameters() {
List<Object[]> parameters = new ArrayList<>();
for (File os : new File("src/test/resources/conf").listFiles()) {
for (File os : new File("src/intTest/resources/conf").listFiles()) {
for (File version : os.listFiles()) {
parameters.add(new Object[] { os.getName(), version.getName() });
}
@@ -333,25 +333,22 @@ class SysVinitLaunchScriptIT {
private LaunchScriptTestContainer(String os, String version, String testScript) {
super(new ImageFromDockerfile("spring-boot-launch-script/" + os.toLowerCase() + "-" + version)
.withFileFromFile("Dockerfile",
new File("src/test/resources/conf/" + os + "/" + version + "/Dockerfile"))
.withFileFromFile("spring-boot-launch-script-tests.jar", findApplication())
.withFileFromFile("test-functions.sh", new File("src/test/resources/scripts/test-functions.sh")));
withCopyFileToContainer(MountableFile.forHostPath("src/test/resources/scripts/" + testScript),
new File("src/intTest/resources/conf/" + os + "/" + version + "/Dockerfile"))
.withFileFromFile("app.jar", findApplication()).withFileFromFile("test-functions.sh",
new File("src/intTest/resources/scripts/test-functions.sh")));
withCopyFileToContainer(MountableFile.forHostPath("src/intTest/resources/scripts/" + testScript),
"/" + testScript);
withCommand("/bin/bash", "-c", "chmod +x " + testScript + " && ./" + testScript);
withStartupTimeout(Duration.ofMinutes(10));
}
private static File findApplication() {
File targetDir = new File("target");
for (File file : targetDir.listFiles()) {
if (file.getName().startsWith("spring-boot-launch-script-tests") && file.getName().endsWith(".jar")
&& !file.getName().endsWith("-sources.jar")) {
return file;
}
File appJar = new File("build/app/build/libs/app.jar");
if (appJar.isFile()) {
return appJar;
}
throw new IllegalStateException(
"Could not find test application in target directory. Have you built it (mvn package)?");
"Could not find test application in build/app/build/libs directory. Have you built it?");
}
}

View File

@@ -7,5 +7,5 @@ RUN yum install -y wget && \
https://cdn.azul.com/zulu/bin/zulu8.21.0.1-jdk8.0.131-linux.x86_64.rpm && \
yum --nogpg localinstall -y jdk.rpm && \
rm -f jdk.rpm
ADD spring-boot-launch-script-tests.jar /spring-boot-launch-script-tests.jar
ADD app.jar /app.jar
ADD test-functions.sh /test-functions.sh

View File

@@ -6,5 +6,5 @@ RUN apt-get update && \
curl -L https://github.com/AdoptOpenJDK/openjdk8-binaries/releases/download/jdk8u202-b08/OpenJDK8U-jdk_x64_linux_hotspot_8u202b08.tar.gz | tar zx --strip-components=1
ENV JAVA_HOME /opt/openjdk
ENV PATH $JAVA_HOME/bin:$PATH
ADD spring-boot-launch-script-tests.jar /spring-boot-launch-script-tests.jar
ADD app.jar /app.jar
ADD test-functions.sh /test-functions.sh

View File

@@ -6,5 +6,5 @@ RUN apt-get update && \
curl -L https://github.com/AdoptOpenJDK/openjdk8-binaries/releases/download/jdk8u202-b08/OpenJDK8U-jdk_x64_linux_hotspot_8u202b08.tar.gz | tar zx --strip-components=1
ENV JAVA_HOME /opt/openjdk
ENV PATH $JAVA_HOME/bin:$PATH
ADD spring-boot-launch-script-tests.jar /spring-boot-launch-script-tests.jar
ADD app.jar /app.jar
ADD test-functions.sh /test-functions.sh

View File

@@ -1,15 +1,15 @@
install_service() {
mkdir /test-service
mv /spring-boot-launch-script-tests.jar /test-service/spring-boot-app.jar
mv /app.jar /test-service/spring-boot-app.jar
chmod +x /test-service/spring-boot-app.jar
ln -s /test-service/spring-boot-app.jar /etc/init.d/spring-boot-app
}
install_double_link_service() {
mkdir /test-service
mv /spring-boot-launch-script-tests.jar /test-service/
chmod +x /test-service/spring-boot-launch-script-tests.jar
ln -s /test-service/spring-boot-launch-script-tests.jar /test-service/spring-boot-app.jar
mv /app.jar /test-service/
chmod +x /test-service/app.jar
ln -s /test-service/app.jar /test-service/spring-boot-app.jar
ln -s /test-service/spring-boot-app.jar /etc/init.d/spring-boot-app
}
@@ -41,7 +41,7 @@ await_app() {
url=$1
fi
end=$(date +%s)
let "end+=30"
let "end+=600"
until curl -s $url > /dev/null
do
now=$(date +%s)

View File

@@ -0,0 +1,51 @@
plugins {
id 'java'
id 'org.springframework.boot.conventions'
}
description = "Spring Boot Server Tests"
configurations {
testRepository
}
dependencies {
testImplementation project(':spring-boot-project:spring-boot-starters:spring-boot-starter-test')
testImplementation project(':spring-boot-project:spring-boot-tools:spring-boot-test-support')
testImplementation 'com.samskivert:jmustache'
testImplementation 'jakarta.servlet:jakarta.servlet-api'
testImplementation 'org.apache.httpcomponents:httpasyncclient'
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-tools:spring-boot-maven-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-jetty', configuration: 'mavenRepository')
testRepository project(path: ':spring-boot-project:spring-boot-starters:spring-boot-starter-parent', configuration: 'mavenRepository')
testRepository project(path: ':spring-boot-project:spring-boot-starters:spring-boot-starter-tomcat', configuration: 'mavenRepository')
testRepository project(path: ':spring-boot-project:spring-boot-starters:spring-boot-starter-undertow', configuration: 'mavenRepository')
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) {
destinationDir = file("${buildDir}/test-repository")
from {
configurations.testRepository
}
}
test {
dependsOn prepareMavenBinaries, syncTestRepository
}

View File

@@ -1,84 +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-integration-tests</artifactId>
<version>${revision}</version>
</parent>
<artifactId>spring-boot-server-tests</artifactId>
<packaging>jar</packaging>
<name>Spring Boot Server Tests</name>
<properties>
<main.basedir>${basedir}/../../..</main.basedir>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test-support</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.samskivert</groupId>
<artifactId>jmustache</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.maven.shared</groupId>
<artifactId>maven-invoker</artifactId>
<version>3.0.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-webapp</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<systemPropertyVariables>
<maven.home>${maven.home}</maven.home>
<repository>${repository}</repository>
</systemPropertyVariables>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -95,7 +95,7 @@ abstract class AbstractApplicationLauncher implements BeforeEachCallback, AfterE
}
private int awaitServerPort(Process process, File serverPortFile) throws Exception {
Awaitility.waitAtMost(Duration.ofSeconds(30)).until(serverPortFile::length, (length) -> {
Awaitility.waitAtMost(Duration.ofSeconds(180)).until(serverPortFile::length, (length) -> {
if (!process.isAlive()) {
throw new IllegalStateException("Application failed to start");
}

View File

@@ -36,13 +36,11 @@ import org.apache.maven.shared.invoker.InvocationResult;
import org.apache.maven.shared.invoker.MavenInvocationException;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.StringUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Builds a Spring Boot application using Maven. To use this class, the {@code maven.home}
* system property must be set.
* Builds a Spring Boot application using Maven.
*
* @author Andy Wilkinson
*/
@@ -121,12 +119,9 @@ class ApplicationBuilder {
}
private File writeSettingsXml(File appFolder) throws IOException {
String repository = System.getProperty("repository");
if (!StringUtils.hasText(repository)) {
return null;
}
Map<String, Object> context = new HashMap<>();
context.put("repository", repository);
context.put("repository", new File("build/test-repository").toURI().toURL());
context.put("localRepository", new File("build/local-m2-repository").getAbsolutePath());
File settingsXml = new File(appFolder, "settings.xml");
try (FileWriter out = new FileWriter(settingsXml);
FileReader templateReader = new FileReader("src/test/resources/settings-template.xml")) {
@@ -160,7 +155,9 @@ class ApplicationBuilder {
if (settingsXml != null) {
invocation.setUserSettingsFile(settingsXml);
}
InvocationResult execute = new DefaultInvoker().execute(invocation);
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);
}

View File

@@ -27,6 +27,8 @@ import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.client.StandardHttpRequestRetryHandler;
import org.junit.jupiter.api.extension.AfterAllCallback;
import org.junit.jupiter.api.extension.Extension;
import org.junit.jupiter.api.extension.ExtensionContext;
@@ -39,6 +41,7 @@ import org.junit.platform.commons.util.ReflectionUtils;
import org.springframework.boot.testsupport.BuildOutput;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.util.FileSystemUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.client.ResponseErrorHandler;
@@ -154,7 +157,8 @@ class EmbeddedServerContainerInvocationContextProvider
@Override
public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext)
throws ParameterResolutionException {
RestTemplate rest = new RestTemplate();
RestTemplate rest = new RestTemplate(new HttpComponentsClientHttpRequestFactory(
HttpClients.custom().setRetryHandler(new StandardHttpRequestRetryHandler(10, false)).build()));
rest.setErrorHandler(new ResponseErrorHandler() {
@Override

View File

@@ -80,6 +80,7 @@ class ExplodedApplicationLauncher extends AbstractApplicationLauncher {
extracted.mkdirs();
}
else {
extracted.getParentFile().mkdirs();
FileOutputStream extractedOutputStream = new FileOutputStream(extracted);
StreamUtils.copy(jarFile.getInputStream(jarEntry), extractedOutputStream);
extractedOutputStream.close();

View File

@@ -16,43 +16,28 @@
package org.springframework.boot.context.embedded;
import java.io.File;
import java.io.FileReader;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathFactory;
import org.xml.sax.InputSource;
import org.springframework.util.StringUtils;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;
/**
* Provides access to dependency versions by querying the project's pom.
* Provides access to the current Boot version by referring to {@code gradle.properties}.
*
* @author Andy Wilkinson
*/
final class Versions {
private static final String PROPERTIES = "/*[local-name()='project']/*[local-name()='properties']";
private Versions() {
}
static String getBootVersion() {
String baseDir = StringUtils.cleanPath(new File(".").getAbsolutePath());
String mainBaseDir = evaluateExpression("pom.xml", PROPERTIES + "/*[local-name()='main.basedir']/text()");
mainBaseDir = mainBaseDir.replace("${basedir}", baseDir);
return evaluateExpression(mainBaseDir + "/pom.xml", PROPERTIES + "/*[local-name()='revision']/text()");
}
private static String evaluateExpression(String file, String expression) {
try {
InputSource source = new InputSource(new FileReader(file));
XPath xpath = XPathFactory.newInstance().newXPath();
return xpath.compile(expression).evaluate(source);
Properties gradleProperties = new Properties();
try (FileInputStream input = new FileInputStream("../../../gradle.properties")) {
gradleProperties.load(input);
return gradleProperties.getProperty("version");
}
catch (Exception ex) {
throw new IllegalStateException("Failed to evaluate expression", ex);
catch (IOException ex) {
throw new RuntimeException(ex);
}
}

View File

@@ -1,6 +1,7 @@
<?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>