Merge branch '2.5.x' into 2.6.x

Closes gh-29905
pull/30003/head
Brian Clozel 3 years ago
commit 16c47595e7

@ -786,6 +786,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);
} }
} }
} }

@ -43,6 +43,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 {
@ -92,6 +93,17 @@ class SpringApplicationShutdownHook implements Runnable {
} }
} }
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 {
@ -1247,6 +1250,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…
Cancel
Save