Merge branch '2.7.x'

This commit is contained in:
Brian Clozel 2022-02-18 13:56:11 +01:00
commit 78dde15da4
4 changed files with 66 additions and 1 deletions

View File

@ -774,6 +774,7 @@ public class SpringApplication {
reportFailure(getExceptionReporters(context), exception);
if (context != null) {
context.close();
shutdownHook.deregisterFailedApplicationContext(context);
}
}
}

View File

@ -42,6 +42,7 @@ import org.springframework.util.Assert;
*
* @author Andy Wilkinson
* @author Phillip Webb
* @author Brian Clozel
*/
class SpringApplicationShutdownHook implements Runnable {
@ -86,6 +87,17 @@ class SpringApplicationShutdownHook implements Runnable {
Runtime.getRuntime().addShutdownHook(new Thread(this, "SpringApplicationShutdownHook"));
}
void deregisterFailedApplicationContext(ConfigurableApplicationContext applicationContext) {
synchronized (SpringApplicationShutdownHook.class) {
if (!applicationContext.isActive()) {
SpringApplicationShutdownHook.this.contexts.remove(applicationContext);
}
else {
throw new IllegalStateException("Cannot unregister active application context");
}
}
}
@Override
public void run() {
Set<ConfigurableApplicationContext> contexts;

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2021 the original author or authors.
* Copyright 2012-2022 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.
@ -26,6 +26,7 @@ import java.util.concurrent.CountDownLatch;
import org.awaitility.Awaitility;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
@ -36,12 +37,14 @@ import org.springframework.context.support.GenericApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
/**
* Tests for {@link SpringApplicationShutdownHook}.
*
* @author Phillip Webb
* @author Andy Wilkinson
* @author Brian Clozel
*/
class SpringApplicationShutdownHookTests {
@ -154,6 +157,29 @@ class SpringApplicationShutdownHookTests {
.withMessage("Shutdown in progress");
}
@Test
void failsWhenDeregisterActiveContext() {
TestSpringApplicationShutdownHook shutdownHook = new TestSpringApplicationShutdownHook();
ConfigurableApplicationContext context = new GenericApplicationContext();
shutdownHook.registerApplicationContext(context);
context.refresh();
assertThatThrownBy(() -> shutdownHook.deregisterFailedApplicationContext(context))
.isInstanceOf(IllegalStateException.class);
assertThat(shutdownHook.isApplicationContextRegistered(context)).isTrue();
}
@Test
void deregistersFailedContext() {
TestSpringApplicationShutdownHook shutdownHook = new TestSpringApplicationShutdownHook();
GenericApplicationContext context = new GenericApplicationContext();
shutdownHook.registerApplicationContext(context);
context.registerBean(FailingBean.class);
assertThatThrownBy(context::refresh).isInstanceOf(BeanCreationException.class);
assertThat(shutdownHook.isApplicationContextRegistered(context)).isTrue();
shutdownHook.deregisterFailedApplicationContext(context);
assertThat(shutdownHook.isApplicationContextRegistered(context)).isFalse();
}
static class TestSpringApplicationShutdownHook extends SpringApplicationShutdownHook {
private boolean runtimeShutdownHookAdded;
@ -259,4 +285,13 @@ class SpringApplicationShutdownHookTests {
}
static class FailingBean implements InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
throw new IllegalArgumentException("test failure");
}
}
}

View File

@ -16,10 +16,12 @@
package org.springframework.boot;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
@ -150,6 +152,7 @@ import static org.mockito.Mockito.spy;
* @author Marten Deinum
* @author Nguyen Bao Sach
* @author Chris Bono
* @author Brian Clozel
*/
@ExtendWith(OutputCaptureExtension.class)
class SpringApplicationTests {
@ -1224,6 +1227,20 @@ class SpringApplicationTests {
assertThat(application.getEnvironmentPrefix()).isEqualTo("my");
}
@Test
void deregistersShutdownHookForFailedApplicationContext() {
SpringApplication application = new SpringApplication(BrokenPostConstructConfig.class);
List<ApplicationEvent> events = new ArrayList<>();
application.addListeners(events::add);
application.setWebApplicationType(WebApplicationType.NONE);
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(application::run);
assertThat(events).hasAtLeastOneElementOfType(ApplicationFailedEvent.class);
ApplicationFailedEvent failure = events.stream().filter((event) -> event instanceof ApplicationFailedEvent)
.map(ApplicationFailedEvent.class::cast).findFirst().get();
assertThat(SpringApplicationShutdownHookInstance.get())
.didNotRegisterApplicationContext(failure.getApplicationContext());
}
private <S extends AvailabilityState> ArgumentMatcher<ApplicationEvent> isAvailabilityChangeEventWithState(
S state) {
return (argument) -> (argument instanceof AvailabilityChangeEvent<?>)