Skip to content

Commit b92016b

Browse files
venilnoronhaspencergibb
authored andcommitted
Moved RefreshEndpoint logic to ContextRefresher.
Fixes spring-projectsgh-101
1 parent f29198b commit b92016b

File tree

5 files changed

+245
-201
lines changed

5 files changed

+245
-201
lines changed

spring-cloud-context/src/main/java/org/springframework/cloud/autoconfigure/RefreshAutoConfiguration.java

+10-1
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@
2222
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
2323
import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration;
2424
import org.springframework.cloud.context.environment.EnvironmentManager;
25+
import org.springframework.cloud.context.refresh.ContextRefresher;
2526
import org.springframework.cloud.context.scope.refresh.RefreshScope;
2627
import org.springframework.cloud.logging.LoggingRebinder;
28+
import org.springframework.context.ConfigurableApplicationContext;
2729
import org.springframework.context.annotation.Bean;
2830
import org.springframework.context.annotation.Configuration;
2931
import org.springframework.core.env.ConfigurableEnvironment;
@@ -33,7 +35,7 @@
3335
* the Environment (e.g. rebinding logger levels).
3436
*
3537
* @author Dave Syer
36-
*
38+
* @author Venil Noronha
3739
*/
3840
@Configuration
3941
@ConditionalOnClass(RefreshScope.class)
@@ -58,4 +60,11 @@ public EnvironmentManager environmentManager(ConfigurableEnvironment environment
5860
return new EnvironmentManager(environment);
5961
}
6062

63+
@Bean
64+
@ConditionalOnMissingBean
65+
public ContextRefresher contextRefresher(ConfigurableApplicationContext context,
66+
RefreshScope scope) {
67+
return new ContextRefresher(context, scope);
68+
}
69+
6170
}

spring-cloud-context/src/main/java/org/springframework/cloud/autoconfigure/RefreshEndpointAutoConfiguration.java

+8-4
Original file line numberDiff line numberDiff line change
@@ -35,18 +35,23 @@
3535
import org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration;
3636
import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
3737
import org.springframework.cloud.context.properties.ConfigurationPropertiesRebinder;
38+
import org.springframework.cloud.context.refresh.ContextRefresher;
3839
import org.springframework.cloud.context.restart.RestartEndpoint;
3940
import org.springframework.cloud.context.scope.refresh.RefreshScope;
4041
import org.springframework.cloud.endpoint.RefreshEndpoint;
4142
import org.springframework.cloud.endpoint.event.RefreshEventListener;
4243
import org.springframework.cloud.health.RefreshScopeHealthIndicator;
4344
import org.springframework.context.ApplicationListener;
44-
import org.springframework.context.ConfigurableApplicationContext;
4545
import org.springframework.context.annotation.Bean;
4646
import org.springframework.context.annotation.Configuration;
4747
import org.springframework.core.env.ConfigurableEnvironment;
4848
import org.springframework.integration.monitor.IntegrationMBeanExporter;
4949

50+
/**
51+
* @author Dave Syer
52+
* @author Spencer Gibb
53+
* @author Venil Noronha
54+
*/
5055
@Configuration
5156
@ConditionalOnClass(Endpoint.class)
5257
@AutoConfigureAfter(EndpointAutoConfiguration.class)
@@ -112,9 +117,8 @@ protected static class RefreshEndpointConfiguration {
112117

113118
@Bean
114119
@ConditionalOnMissingBean
115-
public RefreshEndpoint refreshEndpoint(ConfigurableApplicationContext context,
116-
RefreshScope scope) {
117-
RefreshEndpoint endpoint = new RefreshEndpoint(context, scope);
120+
public RefreshEndpoint refreshEndpoint(ContextRefresher contextRefresher) {
121+
RefreshEndpoint endpoint = new RefreshEndpoint(contextRefresher);
118122
return endpoint;
119123
}
120124

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
package org.springframework.cloud.context.refresh;
2+
3+
import java.util.ArrayList;
4+
import java.util.Arrays;
5+
import java.util.HashMap;
6+
import java.util.HashSet;
7+
import java.util.List;
8+
import java.util.Map;
9+
import java.util.Set;
10+
11+
import org.springframework.boot.Banner.Mode;
12+
import org.springframework.boot.builder.SpringApplicationBuilder;
13+
import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
14+
import org.springframework.cloud.context.scope.refresh.RefreshScope;
15+
import org.springframework.context.ApplicationContext;
16+
import org.springframework.context.ConfigurableApplicationContext;
17+
import org.springframework.context.annotation.Configuration;
18+
import org.springframework.core.env.CompositePropertySource;
19+
import org.springframework.core.env.ConfigurableEnvironment;
20+
import org.springframework.core.env.EnumerablePropertySource;
21+
import org.springframework.core.env.MapPropertySource;
22+
import org.springframework.core.env.MutablePropertySources;
23+
import org.springframework.core.env.PropertySource;
24+
import org.springframework.core.env.StandardEnvironment;
25+
import org.springframework.web.context.support.StandardServletEnvironment;
26+
27+
/**
28+
* @author Dave Syer
29+
* @author Venil Noronha
30+
*/
31+
public class ContextRefresher {
32+
33+
private static final String REFRESH_ARGS_PROPERTY_SOURCE = "refreshArgs";
34+
35+
private Set<String> standardSources = new HashSet<String>(
36+
Arrays.asList(StandardEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME,
37+
StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME,
38+
StandardServletEnvironment.JNDI_PROPERTY_SOURCE_NAME,
39+
StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME,
40+
StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME));
41+
42+
private ConfigurableApplicationContext context;
43+
private RefreshScope scope;
44+
45+
public ContextRefresher(ConfigurableApplicationContext context, RefreshScope scope) {
46+
this.context = context;
47+
this.scope = scope;
48+
}
49+
50+
public synchronized Set<String> refresh() {
51+
Map<String, Object> before = extract(
52+
this.context.getEnvironment().getPropertySources());
53+
addConfigFilesToEnvironment();
54+
Set<String> keys = changes(before,
55+
extract(this.context.getEnvironment().getPropertySources())).keySet();
56+
this.context.publishEvent(new EnvironmentChangeEvent(keys));
57+
this.scope.refreshAll();
58+
return keys;
59+
}
60+
61+
private void addConfigFilesToEnvironment() {
62+
ConfigurableApplicationContext capture = null;
63+
try {
64+
StandardEnvironment environment = copyEnvironment(
65+
this.context.getEnvironment());
66+
capture = new SpringApplicationBuilder(Empty.class).bannerMode(Mode.OFF)
67+
.web(false).environment(environment).run();
68+
if (environment.getPropertySources().contains(REFRESH_ARGS_PROPERTY_SOURCE)) {
69+
environment.getPropertySources().remove(REFRESH_ARGS_PROPERTY_SOURCE);
70+
}
71+
MutablePropertySources target = this.context.getEnvironment()
72+
.getPropertySources();
73+
String targetName = null;
74+
for (PropertySource<?> source : environment.getPropertySources()) {
75+
String name = source.getName();
76+
if (target.contains(name)) {
77+
targetName = name;
78+
}
79+
if (!this.standardSources.contains(name)) {
80+
if (target.contains(name)) {
81+
target.replace(name, source);
82+
}
83+
else {
84+
if (targetName != null) {
85+
target.addAfter(targetName, source);
86+
}
87+
else {
88+
if (target.contains("defaultProperties")) {
89+
target.addBefore("defaultProperties", source);
90+
}
91+
else {
92+
target.addLast(source);
93+
}
94+
}
95+
}
96+
}
97+
}
98+
}
99+
finally {
100+
ConfigurableApplicationContext closeable = capture;
101+
while (closeable != null) {
102+
closeable.close();
103+
ApplicationContext parent = closeable.getParent();
104+
if (parent instanceof ConfigurableApplicationContext) {
105+
closeable = (ConfigurableApplicationContext) parent;
106+
}
107+
else {
108+
closeable = null;
109+
}
110+
}
111+
}
112+
}
113+
114+
// Don't use ConfigurableEnvironment.merge() in case there are clashes with property
115+
// source names
116+
private StandardEnvironment copyEnvironment(ConfigurableEnvironment input) {
117+
StandardEnvironment environment = new StandardEnvironment();
118+
MutablePropertySources capturedPropertySources = environment.getPropertySources();
119+
for (PropertySource<?> source : capturedPropertySources) {
120+
capturedPropertySources.remove(source.getName());
121+
}
122+
for (PropertySource<?> source : input.getPropertySources()) {
123+
capturedPropertySources.addLast(source);
124+
}
125+
environment.setActiveProfiles(input.getActiveProfiles());
126+
environment.setDefaultProfiles(input.getDefaultProfiles());
127+
Map<String, Object> map = new HashMap<String, Object>();
128+
map.put("spring.jmx.enabled", false);
129+
map.put("spring.main.sources", "");
130+
capturedPropertySources
131+
.addFirst(new MapPropertySource(REFRESH_ARGS_PROPERTY_SOURCE, map));
132+
return environment;
133+
}
134+
135+
private Map<String, Object> changes(Map<String, Object> before,
136+
Map<String, Object> after) {
137+
Map<String, Object> result = new HashMap<String, Object>();
138+
for (String key : before.keySet()) {
139+
if (!after.containsKey(key)) {
140+
result.put(key, null);
141+
}
142+
else if (!equal(before.get(key), after.get(key))) {
143+
result.put(key, after.get(key));
144+
}
145+
}
146+
for (String key : after.keySet()) {
147+
if (!before.containsKey(key)) {
148+
result.put(key, after.get(key));
149+
}
150+
}
151+
return result;
152+
}
153+
154+
private boolean equal(Object one, Object two) {
155+
if (one == null && two == null) {
156+
return true;
157+
}
158+
if (one == null || two == null) {
159+
return false;
160+
}
161+
return one.equals(two);
162+
}
163+
164+
private Map<String, Object> extract(MutablePropertySources propertySources) {
165+
Map<String, Object> result = new HashMap<String, Object>();
166+
List<PropertySource<?>> sources = new ArrayList<PropertySource<?>>();
167+
for (PropertySource<?> source : propertySources) {
168+
sources.add(0, source);
169+
}
170+
for (PropertySource<?> source : sources) {
171+
if (!this.standardSources.contains(source.getName())) {
172+
extract(source, result);
173+
}
174+
}
175+
return result;
176+
}
177+
178+
private void extract(PropertySource<?> parent, Map<String, Object> result) {
179+
if (parent instanceof CompositePropertySource) {
180+
try {
181+
List<PropertySource<?>> sources = new ArrayList<PropertySource<?>>();
182+
for (PropertySource<?> source : ((CompositePropertySource) parent)
183+
.getPropertySources()) {
184+
sources.add(0, source);
185+
}
186+
for (PropertySource<?> source : sources) {
187+
extract(source, result);
188+
}
189+
}
190+
catch (Exception e) {
191+
return;
192+
}
193+
}
194+
else if (parent instanceof EnumerablePropertySource) {
195+
for (String key : ((EnumerablePropertySource<?>) parent).getPropertyNames()) {
196+
result.put(key, parent.getProperty(key));
197+
}
198+
}
199+
}
200+
201+
@Configuration
202+
protected static class Empty {
203+
204+
}
205+
206+
}

0 commit comments

Comments
 (0)