Merge branch '2.7.x'
This commit is contained in:
commit
78dde15da4
|
|
@ -774,6 +774,7 @@ public class SpringApplication {
|
||||||
reportFailure(getExceptionReporters(context), exception);
|
reportFailure(getExceptionReporters(context), exception);
|
||||||
if (context != null) {
|
if (context != null) {
|
||||||
context.close();
|
context.close();
|
||||||
|
shutdownHook.deregisterFailedApplicationContext(context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ import org.springframework.util.Assert;
|
||||||
*
|
*
|
||||||
* @author Andy Wilkinson
|
* @author Andy Wilkinson
|
||||||
* @author Phillip Webb
|
* @author Phillip Webb
|
||||||
|
* @author Brian Clozel
|
||||||
*/
|
*/
|
||||||
class SpringApplicationShutdownHook implements Runnable {
|
class SpringApplicationShutdownHook implements Runnable {
|
||||||
|
|
||||||
|
|
@ -86,6 +87,17 @@ class SpringApplicationShutdownHook implements Runnable {
|
||||||
Runtime.getRuntime().addShutdownHook(new Thread(this, "SpringApplicationShutdownHook"));
|
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
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
Set<ConfigurableApplicationContext> contexts;
|
Set<ConfigurableApplicationContext> contexts;
|
||||||
|
|
|
||||||
|
|
@ -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");
|
* 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.
|
||||||
|
|
@ -26,6 +26,7 @@ import java.util.concurrent.CountDownLatch;
|
||||||
import org.awaitility.Awaitility;
|
import org.awaitility.Awaitility;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.BeanCreationException;
|
||||||
import org.springframework.beans.factory.InitializingBean;
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
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.assertThat;
|
||||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for {@link SpringApplicationShutdownHook}.
|
* Tests for {@link SpringApplicationShutdownHook}.
|
||||||
*
|
*
|
||||||
* @author Phillip Webb
|
* @author Phillip Webb
|
||||||
* @author Andy Wilkinson
|
* @author Andy Wilkinson
|
||||||
|
* @author Brian Clozel
|
||||||
*/
|
*/
|
||||||
class SpringApplicationShutdownHookTests {
|
class SpringApplicationShutdownHookTests {
|
||||||
|
|
||||||
|
|
@ -154,6 +157,29 @@ class SpringApplicationShutdownHookTests {
|
||||||
.withMessage("Shutdown in progress");
|
.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 {
|
static class TestSpringApplicationShutdownHook extends SpringApplicationShutdownHook {
|
||||||
|
|
||||||
private boolean runtimeShutdownHookAdded;
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,12 @@
|
||||||
|
|
||||||
package org.springframework.boot;
|
package org.springframework.boot;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
@ -150,6 +152,7 @@ import static org.mockito.Mockito.spy;
|
||||||
* @author Marten Deinum
|
* @author Marten Deinum
|
||||||
* @author Nguyen Bao Sach
|
* @author Nguyen Bao Sach
|
||||||
* @author Chris Bono
|
* @author Chris Bono
|
||||||
|
* @author Brian Clozel
|
||||||
*/
|
*/
|
||||||
@ExtendWith(OutputCaptureExtension.class)
|
@ExtendWith(OutputCaptureExtension.class)
|
||||||
class SpringApplicationTests {
|
class SpringApplicationTests {
|
||||||
|
|
@ -1224,6 +1227,20 @@ class SpringApplicationTests {
|
||||||
assertThat(application.getEnvironmentPrefix()).isEqualTo("my");
|
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(
|
private <S extends AvailabilityState> ArgumentMatcher<ApplicationEvent> isAvailabilityChangeEventWithState(
|
||||||
S state) {
|
S state) {
|
||||||
return (argument) -> (argument instanceof AvailabilityChangeEvent<?>)
|
return (argument) -> (argument instanceof AvailabilityChangeEvent<?>)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue