commit
16c47595e7
|
@ -786,6 +786,7 @@ public class SpringApplication {
|
|||
reportFailure(getExceptionReporters(context), exception);
|
||||
if (context != null) {
|
||||
context.close();
|
||||
shutdownHook.deregisterFailedApplicationContext(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ import org.springframework.util.Assert;
|
|||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
* @author Brian Clozel
|
||||
*/
|
||||
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
|
||||
public void run() {
|
||||
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");
|
||||
* 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");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
@ -1247,6 +1250,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<?>)
|
||||
|
|
Loading…
Reference in New Issue