mirror of
https://github.com/spring-projects/spring-boot.git
synced 2026-09-17 12:09:16 +00:00
Merge branch '4.0.x'
Closes gh-48701
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2012-present 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.
|
||||
*/
|
||||
|
||||
@NullMarked
|
||||
package com.example;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
+13
-4
@@ -27,11 +27,13 @@ import net.bytebuddy.description.modifier.Visibility;
|
||||
import net.bytebuddy.dynamic.DynamicType;
|
||||
import net.bytebuddy.implementation.FixedValue;
|
||||
import org.awaitility.Awaitility;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
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.util.Assert;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@@ -50,32 +52,39 @@ abstract class AbstractDevToolsIntegrationTests {
|
||||
protected final JvmLauncher javaLauncher = new JvmLauncher();
|
||||
|
||||
@TempDir
|
||||
@SuppressWarnings("NullAway.Init")
|
||||
protected static File temp;
|
||||
|
||||
protected LaunchedApplication launchedApplication;
|
||||
private @Nullable LaunchedApplication launchedApplication;
|
||||
|
||||
protected void launchApplication(ApplicationLauncher applicationLauncher, String... args) throws Exception {
|
||||
protected LaunchedApplication launchApplication(ApplicationLauncher applicationLauncher, String... args)
|
||||
throws Exception {
|
||||
this.serverPortFile.delete();
|
||||
this.launchedApplication = applicationLauncher.launchApplication(this.javaLauncher, this.serverPortFile, args);
|
||||
return this.launchedApplication;
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void stopApplication() throws InterruptedException {
|
||||
Assert.notNull(this.launchedApplication, "Application has not been launched");
|
||||
this.launchedApplication.stop();
|
||||
}
|
||||
|
||||
protected int awaitServerPort() throws Exception {
|
||||
LaunchedApplication launchedApplication = this.launchedApplication;
|
||||
Assert.notNull(launchedApplication, "Application has not been launched");
|
||||
int port = Awaitility.waitAtMost(Duration.ofMinutes(3))
|
||||
.until(() -> new ApplicationState(this.serverPortFile, this.launchedApplication),
|
||||
.until(() -> new ApplicationState(this.serverPortFile, launchedApplication),
|
||||
ApplicationState::hasServerPort)
|
||||
.getServerPort();
|
||||
this.serverPortFile.delete();
|
||||
this.launchedApplication.restartRemote(port);
|
||||
launchedApplication.restartRemote(port);
|
||||
Thread.sleep(1000);
|
||||
return port;
|
||||
}
|
||||
|
||||
protected ControllerBuilder controller(String name) {
|
||||
Assert.notNull(this.launchedApplication, "Application has not been launched");
|
||||
return new ControllerBuilder(name, this.launchedApplication.getClassesDirectory());
|
||||
}
|
||||
|
||||
|
||||
+5
-1
@@ -19,7 +19,10 @@ package org.springframework.boot.devtools.tests;
|
||||
import java.io.File;
|
||||
import java.time.Instant;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.devtools.tests.JvmLauncher.LaunchedJvm;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* State of an application.
|
||||
@@ -30,7 +33,7 @@ final class ApplicationState {
|
||||
|
||||
private final Instant launchTime;
|
||||
|
||||
private final Integer serverPort;
|
||||
private final @Nullable Integer serverPort;
|
||||
|
||||
private final FileContents out;
|
||||
|
||||
@@ -56,6 +59,7 @@ final class ApplicationState {
|
||||
}
|
||||
|
||||
int getServerPort() {
|
||||
Assert.notNull(this.serverPort, "No server port is available");
|
||||
return this.serverPort;
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -125,10 +125,10 @@ class DevToolsIntegrationTests extends AbstractDevToolsIntegrationTests {
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("parameters")
|
||||
void deleteAController(ApplicationLauncher applicationLauncher) throws Exception {
|
||||
launchApplication(applicationLauncher);
|
||||
LaunchedApplication launchedApplication = launchApplication(applicationLauncher);
|
||||
String urlBase = "http://localhost:" + awaitServerPort();
|
||||
assertThat(this.template.getForObject(urlBase + "/one", String.class)).isEqualTo("one");
|
||||
assertThat(new File(this.launchedApplication.getClassesDirectory(), "com/example/ControllerOne.class").delete())
|
||||
assertThat(new File(launchedApplication.getClassesDirectory(), "com/example/ControllerOne.class").delete())
|
||||
.isTrue();
|
||||
urlBase = "http://localhost:" + awaitServerPort();
|
||||
assertThat(this.template.getForEntity(urlBase + "/one", String.class).getStatusCode())
|
||||
@@ -139,7 +139,7 @@ class DevToolsIntegrationTests extends AbstractDevToolsIntegrationTests {
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("parameters")
|
||||
void createAControllerAndThenDeleteIt(ApplicationLauncher applicationLauncher) throws Exception {
|
||||
launchApplication(applicationLauncher);
|
||||
LaunchedApplication launchedApplication = launchApplication(applicationLauncher);
|
||||
String urlBase = "http://localhost:" + awaitServerPort();
|
||||
assertThat(this.template.getForObject(urlBase + "/one", String.class)).isEqualTo("one");
|
||||
assertThat(this.template.getForEntity(urlBase + "/two", String.class).getStatusCode())
|
||||
@@ -148,7 +148,7 @@ class DevToolsIntegrationTests extends AbstractDevToolsIntegrationTests {
|
||||
urlBase = "http://localhost:" + awaitServerPort();
|
||||
assertThat(this.template.getForObject(urlBase + "/one", String.class)).isEqualTo("one");
|
||||
assertThat(this.template.getForObject(urlBase + "/two", String.class)).isEqualTo("two");
|
||||
assertThat(new File(this.launchedApplication.getClassesDirectory(), "com/example/ControllerTwo.class").delete())
|
||||
assertThat(new File(launchedApplication.getClassesDirectory(), "com/example/ControllerTwo.class").delete())
|
||||
.isTrue();
|
||||
urlBase = "http://localhost:" + awaitServerPort();
|
||||
assertThat(this.template.getForEntity(urlBase + "/two", String.class).getStatusCode())
|
||||
|
||||
+5
-3
@@ -21,6 +21,8 @@ import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
|
||||
/**
|
||||
@@ -36,11 +38,11 @@ class FileContents {
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
String get() {
|
||||
@Nullable String get() {
|
||||
return get(Function.identity());
|
||||
}
|
||||
|
||||
<T> T get(Function<String, T> transformer) {
|
||||
<T> @Nullable T get(Function<String, T> transformer) {
|
||||
if ((!this.file.exists()) || this.file.length() == 0) {
|
||||
return null;
|
||||
}
|
||||
@@ -53,7 +55,7 @@ class FileContents {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
public @Nullable String toString() {
|
||||
return get();
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -24,6 +24,7 @@ import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.junit.jupiter.api.extension.BeforeTestExecutionCallback;
|
||||
import org.junit.jupiter.api.extension.Extension;
|
||||
import org.junit.jupiter.api.extension.ExtensionContext;
|
||||
@@ -43,7 +44,7 @@ class JvmLauncher implements BeforeTestExecutionCallback {
|
||||
|
||||
private final BuildOutput buildOutput = new BuildOutput(getClass());
|
||||
|
||||
private File outputDirectory;
|
||||
private @Nullable File outputDirectory;
|
||||
|
||||
@Override
|
||||
public void beforeTestExecution(ExtensionContext context) throws Exception {
|
||||
|
||||
+6
-4
@@ -20,6 +20,8 @@ import java.io.File;
|
||||
import java.time.Instant;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* An application launched by {@link ApplicationLauncher}.
|
||||
*
|
||||
@@ -35,14 +37,14 @@ class LaunchedApplication {
|
||||
|
||||
private final Process localProcess;
|
||||
|
||||
private Process remoteProcess;
|
||||
private @Nullable Process remoteProcess;
|
||||
|
||||
private final Instant launchTime = Instant.now();
|
||||
|
||||
private final BiFunction<Integer, File, Process> remoteProcessRestarter;
|
||||
private final @Nullable BiFunction<Integer, File, Process> remoteProcessRestarter;
|
||||
|
||||
LaunchedApplication(File classesDirectory, File standardOut, File standardError, Process localProcess,
|
||||
Process remoteProcess, BiFunction<Integer, File, Process> remoteProcessRestarter) {
|
||||
@Nullable Process remoteProcess, @Nullable BiFunction<Integer, File, Process> remoteProcessRestarter) {
|
||||
this.classesDirectory = classesDirectory;
|
||||
this.standardOut = standardOut;
|
||||
this.standardError = standardError;
|
||||
@@ -63,7 +65,7 @@ class LaunchedApplication {
|
||||
stop(this.remoteProcess);
|
||||
}
|
||||
|
||||
private void stop(Process process) throws InterruptedException {
|
||||
private void stop(@Nullable Process process) throws InterruptedException {
|
||||
if (process != null) {
|
||||
process.destroy();
|
||||
process.waitFor();
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2012-present 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.
|
||||
*/
|
||||
|
||||
@NullMarked
|
||||
package org.springframework.boot.devtools.tests;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
+4
-2
@@ -24,6 +24,8 @@ import java.util.Deque;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
@@ -105,11 +107,11 @@ public class MockClientHttpRequestFactory implements ClientHttpRequestFactory {
|
||||
|
||||
private final int delay;
|
||||
|
||||
private final byte[] payload;
|
||||
private final byte @Nullable [] payload;
|
||||
|
||||
private final HttpStatus status;
|
||||
|
||||
Response(int delay, byte[] payload, HttpStatus status) {
|
||||
Response(int delay, byte @Nullable [] payload, HttpStatus status) {
|
||||
this.delay = delay;
|
||||
this.payload = payload;
|
||||
this.status = status;
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2012-present 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.
|
||||
*/
|
||||
|
||||
@NullMarked
|
||||
package org.springframework.boot.devtools.test;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
+6
-2
@@ -18,6 +18,8 @@ package org.springframework.boot.loader.launch;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Fake launcher in the {@code org.springframework.boot.loader.launch} package used in
|
||||
* {@code MainMethodTests}.
|
||||
@@ -26,13 +28,15 @@ import java.util.function.Consumer;
|
||||
*/
|
||||
public final class FakeJarLauncher {
|
||||
|
||||
public static Consumer<String[]> action;
|
||||
public static @Nullable Consumer<String[]> action;
|
||||
|
||||
private FakeJarLauncher() {
|
||||
}
|
||||
|
||||
public static void main(String... args) {
|
||||
action.accept(args);
|
||||
if (action != null) {
|
||||
action.accept(args);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2012-present 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.
|
||||
*/
|
||||
|
||||
@NullMarked
|
||||
package org.springframework.boot.loader.launch;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
Reference in New Issue
Block a user