Skip to content

Commit 57125c0

Browse files
committed
AbstractApplicationContext refinements (backported from 4.2.2)
Shutdown hook triggers doClose within startupShutdownMonitor; allow for re-refresh and re-close. Issue: SPR-13556 Issue: SPR-13425
1 parent 50c5942 commit 57125c0

File tree

2 files changed

+32
-22
lines changed

2 files changed

+32
-22
lines changed

spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2014 the original author or authors.
2+
* Copyright 2002-2015 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -162,6 +162,9 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
162162
/** Parent context */
163163
private ApplicationContext parent;
164164

165+
/** Environment used by this context */
166+
private ConfigurableEnvironment environment;
167+
165168
/** BeanFactoryPostProcessors to apply on refresh */
166169
private final List<BeanFactoryPostProcessor> beanFactoryPostProcessors =
167170
new ArrayList<BeanFactoryPostProcessor>();
@@ -194,10 +197,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
194197
private ApplicationEventMulticaster applicationEventMulticaster;
195198

196199
/** Statically specified listeners */
197-
private Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<ApplicationListener<?>>();
198-
199-
/** Environment used by this context; initialized by {@link #createEnvironment()} */
200-
private ConfigurableEnvironment environment;
200+
private final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<ApplicationListener<?>>();
201201

202202

203203
/**
@@ -352,7 +352,7 @@ ApplicationEventMulticaster getApplicationEventMulticaster() throws IllegalState
352352
* @return the internal LifecycleProcessor (never {@code null})
353353
* @throws IllegalStateException if the context has not been initialized yet
354354
*/
355-
LifecycleProcessor getLifecycleProcessor() {
355+
LifecycleProcessor getLifecycleProcessor() throws IllegalStateException {
356356
if (this.lifecycleProcessor == null) {
357357
throw new IllegalStateException("LifecycleProcessor not initialized - " +
358358
"call 'refresh' before invoking lifecycle methods via the context: " + this);
@@ -504,6 +504,7 @@ public void refresh() throws BeansException, IllegalStateException {
504504
*/
505505
protected void prepareRefresh() {
506506
this.startupDate = System.currentTimeMillis();
507+
this.closed.set(false);
507508
this.active.set(true);
508509

509510
if (logger.isInfoEnabled()) {
@@ -721,11 +722,12 @@ protected void registerListeners() {
721722
for (ApplicationListener<?> listener : getApplicationListeners()) {
722723
getApplicationEventMulticaster().addApplicationListener(listener);
723724
}
725+
724726
// Do not initialize FactoryBeans here: We need to leave all regular beans
725727
// uninitialized to let post-processors apply to them!
726728
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
727-
for (String lisName : listenerBeanNames) {
728-
getApplicationEventMulticaster().addApplicationListenerBean(lisName);
729+
for (String listenerBeanName : listenerBeanNames) {
730+
getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
729731
}
730732
}
731733

@@ -801,7 +803,9 @@ public void registerShutdownHook() {
801803
this.shutdownHook = new Thread() {
802804
@Override
803805
public void run() {
804-
doClose();
806+
synchronized (startupShutdownMonitor) {
807+
doClose();
808+
}
805809
}
806810
};
807811
Runtime.getRuntime().addShutdownHook(this.shutdownHook);

spring-context/src/test/java/org/springframework/context/support/ClassPathXmlApplicationContextTests.java

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2015 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -49,7 +49,7 @@
4949
* @author Juergen Hoeller
5050
* @author Chris Beams
5151
*/
52-
public final class ClassPathXmlApplicationContextTests {
52+
public class ClassPathXmlApplicationContextTests {
5353

5454
private static final String PATH = "/org/springframework/context/support/";
5555
private static final String RESOURCE_CONTEXT = PATH + "ClassPathXmlApplicationContextTests-resource.xml";
@@ -82,13 +82,23 @@ public void testSingleConfigLocation() {
8282
@Test
8383
public void testMultipleConfigLocations() {
8484
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(
85-
new String[] { FQ_CONTEXT_B, FQ_CONTEXT_C, FQ_CONTEXT_A});
85+
FQ_CONTEXT_B, FQ_CONTEXT_C, FQ_CONTEXT_A);
8686
assertTrue(ctx.containsBean("service"));
8787
assertTrue(ctx.containsBean("logicOne"));
8888
assertTrue(ctx.containsBean("logicTwo"));
89+
90+
// re-refresh (after construction refresh)
8991
Service service = (Service) ctx.getBean("service");
9092
ctx.refresh();
9193
assertTrue(service.isProperlyDestroyed());
94+
95+
// regular close call
96+
service = (Service) ctx.getBean("service");
97+
ctx.close();
98+
assertTrue(service.isProperlyDestroyed());
99+
100+
// re-activating and re-closing the context (SPR-13425)
101+
ctx.refresh();
92102
service = (Service) ctx.getBean("service");
93103
ctx.close();
94104
assertTrue(service.isProperlyDestroyed());
@@ -107,16 +117,15 @@ public void testConfigLocationPattern() {
107117

108118
@Test
109119
public void testSingleConfigLocationWithClass() {
110-
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(
111-
SIMPLE_CONTEXT, getClass());
120+
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(SIMPLE_CONTEXT, getClass());
112121
assertTrue(ctx.containsBean("someMessageSource"));
113122
ctx.close();
114123
}
115124

116125
@Test
117126
public void testAliasWithPlaceholder() {
118127
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(
119-
new String[] { FQ_CONTEXT_B, FQ_ALIASED_CONTEXT_C, FQ_CONTEXT_A});
128+
FQ_CONTEXT_B, FQ_ALIASED_CONTEXT_C, FQ_CONTEXT_A);
120129
assertTrue(ctx.containsBean("service"));
121130
assertTrue(ctx.containsBean("logicOne"));
122131
assertTrue(ctx.containsBean("logicTwo"));
@@ -144,16 +153,14 @@ public void testContextWithInvalidValueType() throws IOException {
144153
private void checkExceptionFromInvalidValueType(Throwable ex) throws IOException {
145154
ByteArrayOutputStream baos = new ByteArrayOutputStream();
146155
ex.printStackTrace(new PrintStream(baos));
147-
String dump = FileCopyUtils.copyToString(
148-
new InputStreamReader(new ByteArrayInputStream(baos.toByteArray())));
156+
String dump = FileCopyUtils.copyToString(new InputStreamReader(new ByteArrayInputStream(baos.toByteArray())));
149157
assertTrue(dump.contains("someMessageSource"));
150158
assertTrue(dump.contains("useCodeAsDefaultMessage"));
151159
}
152160

153161
@Test
154162
public void testContextWithInvalidLazyClass() {
155-
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(
156-
INVALID_CLASS_CONTEXT, getClass());
163+
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(INVALID_CLASS_CONTEXT, getClass());
157164
assertTrue(ctx.containsBean("someMessageSource"));
158165
try {
159166
ctx.getBean("someMessageSource");
@@ -167,8 +174,7 @@ public void testContextWithInvalidLazyClass() {
167174

168175
@Test
169176
public void testContextWithClassNameThatContainsPlaceholder() {
170-
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(
171-
CLASS_WITH_PLACEHOLDER_CONTEXT, getClass());
177+
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(CLASS_WITH_PLACEHOLDER_CONTEXT, getClass());
172178
assertTrue(ctx.containsBean("someMessageSource"));
173179
assertTrue(ctx.getBean("someMessageSource") instanceof StaticMessageSource);
174180
ctx.close();
@@ -282,7 +288,7 @@ public void testAliasThatOverridesParent() {
282288
@Test
283289
public void testAliasThatOverridesEarlierBean() {
284290
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(
285-
new String[] {FQ_SIMPLE_CONTEXT, ALIAS_THAT_OVERRIDES_PARENT_CONTEXT});
291+
FQ_SIMPLE_CONTEXT, ALIAS_THAT_OVERRIDES_PARENT_CONTEXT);
286292
Object myMs = ctx.getBean("myMessageSource");
287293
Object someMs2 = ctx.getBean("someMessageSource");
288294
assertSame(myMs, someMs2);

0 commit comments

Comments
 (0)