Moved non-config client related code into commons
Refresh, Bootstrap and encryption features all moved into commons See gh-104
This commit is contained in:
5
pom.xml
5
pom.xml
@@ -41,11 +41,6 @@
|
||||
<artifactId>spring-cloud-config-server</artifactId>
|
||||
<version>1.0.1.BUILD-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-rsa</artifactId>
|
||||
<version>1.0.0.RELEASE</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jgit</groupId>
|
||||
<artifactId>org.eclipse.jgit</artifactId>
|
||||
|
||||
@@ -22,44 +22,28 @@
|
||||
</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
<optional>true</optional>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-commons</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-crypto</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-rsa</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.integration</groupId>
|
||||
<artifactId>spring-integration-jmx</artifactId>
|
||||
<optional>true</optional>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2014 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.autoconfigure;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.actuate.autoconfigure.EndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.endpoint.EnvironmentEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration;
|
||||
import org.springframework.cloud.config.client.RefreshEndpoint;
|
||||
import org.springframework.cloud.context.environment.EnvironmentManager;
|
||||
import org.springframework.cloud.context.environment.EnvironmentManagerMvcEndpoint;
|
||||
import org.springframework.cloud.context.restart.RestartEndpoint;
|
||||
import org.springframework.cloud.context.restart.RestartMvcEndpoint;
|
||||
import org.springframework.cloud.endpoint.GenericPostableMvcEndpoint;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Autoconfiguration for some MVC endpoints governing the application context lifecycle.
|
||||
* Provides restart, pause, resume, refresh (environment) and environment update
|
||||
* endpoints.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass(EnvironmentEndpoint.class)
|
||||
@ConditionalOnProperty(value = "endpoints.env.enabled", matchIfMissing = true)
|
||||
@ConditionalOnWebApplication
|
||||
@ConditionalOnBean(RestartEndpoint.class)
|
||||
@AutoConfigureAfter({ WebMvcAutoConfiguration.class, EndpointAutoConfiguration.class,
|
||||
RefreshAutoConfiguration.class })
|
||||
public class LifecycleMvcEndpointAutoConfiguration {
|
||||
|
||||
@Autowired
|
||||
private RestartEndpoint restartEndpoint;
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(EnvironmentEndpoint.class)
|
||||
public EnvironmentManagerMvcEndpoint environmentManagerEndpoint(
|
||||
EnvironmentEndpoint delegate, EnvironmentManager environment) {
|
||||
return new EnvironmentManagerMvcEndpoint(delegate, environment);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(RefreshEndpoint.class)
|
||||
public MvcEndpoint refreshMvcEndpoint(RefreshEndpoint endpoint) {
|
||||
return new GenericPostableMvcEndpoint(endpoint);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RestartMvcEndpoint restartMvcEndpoint() {
|
||||
return new RestartMvcEndpoint(restartEndpoint);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MvcEndpoint pauseMvcEndpoint(RestartMvcEndpoint restartEndpoint) {
|
||||
return restartEndpoint.getPauseEndpoint();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MvcEndpoint resumeMvcEndpoint(RestartMvcEndpoint restartEndpoint) {
|
||||
return restartEndpoint.getResumeEndpoint();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,215 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2014 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.autoconfigure;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.actuate.autoconfigure.EndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.endpoint.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.InfoEndpoint;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.ConfigurationBeanFactoryMetaData;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor;
|
||||
import org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessorRegistrar;
|
||||
import org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration;
|
||||
import org.springframework.cloud.config.client.RefreshEndpoint;
|
||||
import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
|
||||
import org.springframework.cloud.context.environment.EnvironmentManager;
|
||||
import org.springframework.cloud.context.properties.ConfigurationPropertiesRebinder;
|
||||
import org.springframework.cloud.context.restart.RestartEndpoint;
|
||||
import org.springframework.cloud.context.scope.refresh.RefreshScope;
|
||||
import org.springframework.cloud.logging.LoggingRebinder;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.integration.monitor.IntegrationMBeanExporter;
|
||||
|
||||
/**
|
||||
* Autoconfiguration for the refresh scope and associated features to do with changes in
|
||||
* the Environment (e.g. rebinding logger levels).
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass(RefreshScope.class)
|
||||
@AutoConfigureAfter(WebMvcAutoConfiguration.class)
|
||||
public class RefreshAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public static RefreshScope refreshScope() {
|
||||
return new RefreshScope();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public static LoggingRebinder loggingRebinder() {
|
||||
return new LoggingRebinder();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public EnvironmentManager environmentManager(ConfigurableEnvironment environment) {
|
||||
return new EnvironmentManager(environment);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(InfoEndpoint.class)
|
||||
@ConditionalOnBean(EndpointAutoConfiguration.class)
|
||||
protected static class InfoEndpointRebinderConfiguration implements
|
||||
ApplicationListener<EnvironmentChangeEvent> {
|
||||
|
||||
@Autowired
|
||||
private EndpointAutoConfiguration endpoints;
|
||||
|
||||
@Autowired
|
||||
private ConfigurableEnvironment environment;
|
||||
|
||||
private Map<String, Object> map = new LinkedHashMap<String, Object>();
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(EnvironmentChangeEvent event) {
|
||||
for (String key : event.getKeys()) {
|
||||
if (key.startsWith("info.")) {
|
||||
map.put(key.substring("info.".length()), environment.getProperty(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
public InfoEndpoint infoEndpoint() throws Exception {
|
||||
return new InfoEndpoint(endpoints.infoEndpoint().invoke()) {
|
||||
@Override
|
||||
public Map<String, Object> invoke() {
|
||||
Map<String, Object> info = new LinkedHashMap<String, Object>(
|
||||
super.invoke());
|
||||
info.putAll(map);
|
||||
return info;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnBean(ConfigurationPropertiesBindingPostProcessor.class)
|
||||
protected static class ConfigurationPropertiesRebinderConfiguration implements
|
||||
BeanFactoryAware {
|
||||
|
||||
private BeanFactory context;
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory applicationContext) throws BeansException {
|
||||
context = applicationContext;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ConfigurationPropertiesRebinder configurationPropertiesRebinder() {
|
||||
// Since this is a BeanPostProcessor we have to be super careful not to cause
|
||||
// a cascade of bean instantiation. Knowing the *name* of the beans we need is
|
||||
// super optimal, but a little brittle (unfortunately we have no choice).
|
||||
ConfigurationPropertiesBindingPostProcessor binder = context
|
||||
.getBean(
|
||||
ConfigurationPropertiesBindingPostProcessorRegistrar.BINDER_BEAN_NAME,
|
||||
ConfigurationPropertiesBindingPostProcessor.class);
|
||||
ConfigurationBeanFactoryMetaData metaData = context.getBean(
|
||||
ConfigurationPropertiesBindingPostProcessorRegistrar.BINDER_BEAN_NAME
|
||||
+ ".store", ConfigurationBeanFactoryMetaData.class);
|
||||
ConfigurationPropertiesRebinder rebinder = new ConfigurationPropertiesRebinder(
|
||||
binder);
|
||||
rebinder.setBeanMetaDataStore(metaData);
|
||||
return rebinder;
|
||||
}
|
||||
}
|
||||
|
||||
@ConditionalOnClass(Endpoint.class)
|
||||
protected static class RefreshEndpointsConfiguration {
|
||||
|
||||
@ConditionalOnClass(IntegrationMBeanExporter.class)
|
||||
protected static class RestartEndpointWithIntegration {
|
||||
|
||||
@Autowired(required = false)
|
||||
private IntegrationMBeanExporter exporter;
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public RestartEndpoint restartEndpoint() {
|
||||
RestartEndpoint endpoint = new RestartEndpoint();
|
||||
if (exporter != null) {
|
||||
endpoint.setIntegrationMBeanExporter(exporter);
|
||||
}
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ConditionalOnMissingClass(name = "org.springframework.integration.monitor.IntegrationMBeanExporter")
|
||||
protected static class RestartEndpointWithoutIntegration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public RestartEndpoint restartEndpoint() {
|
||||
return new RestartEndpoint();
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConfigurationProperties("endpoints.pause")
|
||||
public Endpoint<Boolean> pauseEndpoint(RestartEndpoint restartEndpoint) {
|
||||
return restartEndpoint.getPauseEndpoint();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConfigurationProperties("endpoints.resume")
|
||||
public Endpoint<Boolean> resumeEndpoint(RestartEndpoint restartEndpoint) {
|
||||
return restartEndpoint.getResumeEndpoint();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnProperty(value = "endpoints.refresh.enabled", matchIfMissing = true)
|
||||
@ConditionalOnBean(PropertySourceBootstrapConfiguration.class)
|
||||
protected static class RefreshEndpointConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public RefreshEndpoint refreshEndpoint(
|
||||
ConfigurableApplicationContext context, RefreshScope scope) {
|
||||
RefreshEndpoint endpoint = new RefreshEndpoint(context, scope);
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,217 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2014 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.bootstrap;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.builder.ParentContextApplicationContextInitializer;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
import org.springframework.core.env.MutablePropertySources;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A listener that prepares a SpringApplication (e.g. populating its Environment) by
|
||||
* delegating to {@link ApplicationContextInitializer} beans in a separate bootstrap
|
||||
* context. The bootstrap context is a SpringApplication created from sources defined in
|
||||
* spring.factories as {@link BootstrapConfiguration}, and initialized with external
|
||||
* config taken from "bootstrap.properties" (or yml), instead of the normal
|
||||
* "application.properties".
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class BootstrapApplicationListener implements
|
||||
ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered {
|
||||
|
||||
public static final String BOOTSTRAP_PROPERTY_SOURCE_NAME = "bootstrap";
|
||||
|
||||
public static final int DEFAULT_ORDER = Ordered.HIGHEST_PRECEDENCE + 5;
|
||||
|
||||
private int order = DEFAULT_ORDER;
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
|
||||
ConfigurableEnvironment environment = event.getEnvironment();
|
||||
if (!environment.getProperty("spring.cloud.bootstrap.enabled", Boolean.class,
|
||||
true)) {
|
||||
return;
|
||||
}
|
||||
// don't listen to events in a bootstrap context
|
||||
if (environment.getPropertySources().contains("bootstrapInProgress")) {
|
||||
return;
|
||||
}
|
||||
ConfigurableApplicationContext context = bootstrapServiceContext(environment,
|
||||
event.getSpringApplication());
|
||||
apply(context, event.getSpringApplication(), environment);
|
||||
}
|
||||
|
||||
private ConfigurableApplicationContext bootstrapServiceContext(
|
||||
ConfigurableEnvironment environment, final SpringApplication application) {
|
||||
StandardEnvironment bootstrapEnvironment = new StandardEnvironment();
|
||||
MutablePropertySources bootstrapProperties = bootstrapEnvironment
|
||||
.getPropertySources();
|
||||
for (PropertySource<?> source : bootstrapProperties) {
|
||||
bootstrapProperties.remove(source.getName());
|
||||
}
|
||||
String configName = environment
|
||||
.resolvePlaceholders("${spring.cloud.bootstrap.name:bootstrap}");
|
||||
String configLocation = environment
|
||||
.resolvePlaceholders("${spring.cloud.bootstrap.location:}");
|
||||
Map<String, Object> bootstrapMap = new HashMap<>();
|
||||
bootstrapMap.put("spring.config.name", configName);
|
||||
if (StringUtils.hasText(configLocation)) {
|
||||
bootstrapMap.put("spring.config.location", configLocation);
|
||||
}
|
||||
bootstrapProperties.addFirst(new MapPropertySource(
|
||||
BOOTSTRAP_PROPERTY_SOURCE_NAME, bootstrapMap));
|
||||
bootstrapProperties.addFirst(new MapPropertySource("bootstrapInProgress",
|
||||
Collections.<String, Object> emptyMap()));
|
||||
for (PropertySource<?> source : environment.getPropertySources()) {
|
||||
bootstrapProperties.addLast(source);
|
||||
}
|
||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||
// Use names and ensure unique to protect against duplicates
|
||||
List<String> names = SpringFactoriesLoader.loadFactoryNames(
|
||||
BootstrapConfiguration.class, classLoader);
|
||||
// TODO: is it possible or sensible to share a ResourceLoader?
|
||||
SpringApplicationBuilder builder = new SpringApplicationBuilder()
|
||||
.profiles(environment.getActiveProfiles()).showBanner(false)
|
||||
.environment(bootstrapEnvironment)
|
||||
.properties("spring.application.name:" + configName).web(false);
|
||||
List<Class<?>> sources = new ArrayList<>();
|
||||
for (String name : names) {
|
||||
Class<?> cls = ClassUtils.resolveClassName(name, null);
|
||||
try {
|
||||
cls.getDeclaredAnnotations();
|
||||
}
|
||||
catch (Exception e) {
|
||||
continue;
|
||||
}
|
||||
sources.add(cls);
|
||||
}
|
||||
builder.sources(sources.toArray(new Class[sources.size()]));
|
||||
final ConfigurableApplicationContext context = builder.run();
|
||||
// Make the bootstrap context a parent of the app context
|
||||
addAncestorInitializer(application, context);
|
||||
bootstrapProperties.remove("bootstrapInProgress");
|
||||
return context;
|
||||
}
|
||||
|
||||
private void addAncestorInitializer(SpringApplication application,
|
||||
ConfigurableApplicationContext context) {
|
||||
boolean installed = false;
|
||||
for (ApplicationContextInitializer<?> initializer : application.getInitializers()) {
|
||||
if (initializer instanceof AncestorInitializer) {
|
||||
installed = true;
|
||||
// New parent
|
||||
((AncestorInitializer) initializer).setParent(context);
|
||||
}
|
||||
}
|
||||
if (!installed) {
|
||||
application.addInitializers(new AncestorInitializer(context));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void apply(ConfigurableApplicationContext context,
|
||||
SpringApplication application, ConfigurableEnvironment environment) {
|
||||
@SuppressWarnings("rawtypes")
|
||||
List<ApplicationContextInitializer> initializers = getOrderedBeansOfType(context,
|
||||
ApplicationContextInitializer.class);
|
||||
application.addInitializers(initializers
|
||||
.toArray(new ApplicationContextInitializer[initializers.size()]));
|
||||
}
|
||||
|
||||
private <T> List<T> getOrderedBeansOfType(ListableBeanFactory context, Class<T> type) {
|
||||
List<T> result = new ArrayList<T>();
|
||||
for (String name : context.getBeanNamesForType(type)) {
|
||||
result.add(context.getBean(name, type));
|
||||
}
|
||||
AnnotationAwareOrderComparator.sort(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return this.order;
|
||||
}
|
||||
|
||||
private static class AncestorInitializer implements
|
||||
ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
|
||||
|
||||
private ConfigurableApplicationContext parent;
|
||||
|
||||
public AncestorInitializer(ConfigurableApplicationContext parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public void setParent(ConfigurableApplicationContext parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
// Need to run not too late (so not unordered), so that, for instance, the
|
||||
// ContextIdApplicationContextInitializer runs later and picks up the merged
|
||||
// Environment. Also not too early so that other initializers can pick up the
|
||||
// parent (especially the Environment).
|
||||
return Ordered.HIGHEST_PRECEDENCE + 10;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(ConfigurableApplicationContext context) {
|
||||
preemptMerge(
|
||||
context.getEnvironment().getPropertySources(),
|
||||
new MapPropertySource(BOOTSTRAP_PROPERTY_SOURCE_NAME, Collections
|
||||
.<String, Object> emptyMap()));
|
||||
while (context.getParent() != null && context.getParent() != context) {
|
||||
context = (ConfigurableApplicationContext) context.getParent();
|
||||
}
|
||||
new ParentContextApplicationContextInitializer(parent).initialize(context);
|
||||
}
|
||||
|
||||
private void preemptMerge(MutablePropertySources propertySources,
|
||||
PropertySource<?> propertySource) {
|
||||
if (propertySource != null
|
||||
&& !propertySources.contains(propertySource.getName())) {
|
||||
propertySources.addFirst(propertySource);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2014 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. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.bootstrap;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* A marker interface used as a key in <code>META-INF/spring.factories</code>. Entries in
|
||||
* the factories file are used to create the bootstrap application context.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface BootstrapConfiguration {
|
||||
|
||||
/**
|
||||
* Exclude specific auto-configuration classes such that they will never be applied.
|
||||
*/
|
||||
Class<?>[] exclude() default {};
|
||||
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2014 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.bootstrap.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.bind.PropertySourcesPropertyValues;
|
||||
import org.springframework.boot.bind.RelaxedDataBinder;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.bootstrap.BootstrapApplicationListener;
|
||||
import org.springframework.cloud.config.client.ConfigClientProperties;
|
||||
import org.springframework.cloud.config.client.ConfigServicePropertySourceLocator;
|
||||
import org.springframework.cloud.config.client.PropertySourceLocator;
|
||||
import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
|
||||
import org.springframework.cloud.logging.LoggingRebinder;
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.core.env.CompositePropertySource;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.MutablePropertySources;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(PropertySourceBootstrapProperties.class)
|
||||
public class PropertySourceBootstrapConfiguration implements
|
||||
ApplicationContextInitializer<ConfigurableApplicationContext> {
|
||||
|
||||
private static final String BOOTSTRAP_PROPERTY_SOURCE_NAME = BootstrapApplicationListener.BOOTSTRAP_PROPERTY_SOURCE_NAME;
|
||||
|
||||
private static Log logger = LogFactory
|
||||
.getLog(PropertySourceBootstrapConfiguration.class);
|
||||
|
||||
@Autowired(required = false)
|
||||
private List<PropertySourceLocator> propertySourceLocators = new ArrayList<>();
|
||||
|
||||
@Autowired
|
||||
private PropertySourceBootstrapProperties properties;
|
||||
|
||||
public void setPropertySourceLocators(
|
||||
Collection<PropertySourceLocator> propertySourceLocators) {
|
||||
this.propertySourceLocators = new ArrayList<>(propertySourceLocators);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(ConfigurableApplicationContext applicationContext) {
|
||||
CompositePropertySource composite = new CompositePropertySource(
|
||||
BOOTSTRAP_PROPERTY_SOURCE_NAME);
|
||||
AnnotationAwareOrderComparator.sort(propertySourceLocators);
|
||||
boolean empty = true;
|
||||
for (PropertySourceLocator locator : propertySourceLocators) {
|
||||
PropertySource<?> source = null;
|
||||
source = locator.locate(applicationContext.getEnvironment());
|
||||
if (source == null) {
|
||||
continue;
|
||||
}
|
||||
logger.info("Located property source: " + source);
|
||||
composite.addPropertySource(source);
|
||||
empty = false;
|
||||
}
|
||||
if (!empty) {
|
||||
MutablePropertySources propertySources = applicationContext.getEnvironment()
|
||||
.getPropertySources();
|
||||
if (propertySources.contains(BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
|
||||
propertySources.remove(BOOTSTRAP_PROPERTY_SOURCE_NAME);
|
||||
}
|
||||
insertPropertySources(propertySources, composite);
|
||||
setLogLevels(applicationContext.getEnvironment());
|
||||
}
|
||||
}
|
||||
|
||||
private void setLogLevels(ConfigurableEnvironment environment) {
|
||||
LoggingRebinder rebinder = new LoggingRebinder();
|
||||
rebinder.setEnvironment(environment);
|
||||
// We can't fire the event in the ApplicationContext here (too early), but we can
|
||||
// create our own listener and poke it (it doesn't need the key changes)
|
||||
rebinder.onApplicationEvent(new EnvironmentChangeEvent(Collections
|
||||
.<String> emptySet()));
|
||||
}
|
||||
|
||||
private void insertPropertySources(MutablePropertySources propertySources,
|
||||
CompositePropertySource composite) {
|
||||
MutablePropertySources incoming = new MutablePropertySources();
|
||||
incoming.addFirst(composite);
|
||||
PropertySourceBootstrapProperties remoteProperties = new PropertySourceBootstrapProperties();
|
||||
new RelaxedDataBinder(remoteProperties, "spring.cloud.config")
|
||||
.bind(new PropertySourcesPropertyValues(incoming));
|
||||
if (!remoteProperties.isAllowOverride()
|
||||
|| remoteProperties.isOverrideSystemProperties()) {
|
||||
propertySources.addFirst(composite);
|
||||
return;
|
||||
}
|
||||
if (propertySources
|
||||
.contains(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME)) {
|
||||
propertySources.addAfter(
|
||||
StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME,
|
||||
composite);
|
||||
}
|
||||
else {
|
||||
propertySources.addLast(composite);
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
protected static class PropertySourceLocatorConfiguration {
|
||||
|
||||
@Autowired
|
||||
private ConfigurableEnvironment environment;
|
||||
|
||||
@Bean
|
||||
public ConfigClientProperties configClientProperties() {
|
||||
ConfigClientProperties client = new ConfigClientProperties(environment);
|
||||
return client;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(value = "spring.cloud.config.enabled", matchIfMissing = true)
|
||||
public ConfigServicePropertySourceLocator configServicePropertySource() {
|
||||
ConfigServicePropertySourceLocator locator = new ConfigServicePropertySourceLocator(
|
||||
configClientProperties());
|
||||
return locator;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package org.springframework.cloud.bootstrap.config;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
@ConfigurationProperties("spring.cloud.config")
|
||||
public class PropertySourceBootstrapProperties {
|
||||
|
||||
/**
|
||||
* Flag to indicate that the external properties should override system properties.
|
||||
* Default true.
|
||||
*/
|
||||
private boolean overrideSystemProperties = true;
|
||||
|
||||
/**
|
||||
* Flag to indicate that {@link #isSystemPropertiesOverride()
|
||||
* systemPropertiesOverride} can be used. Set to false to prevent users from changing
|
||||
* the default accidentally. Default true.
|
||||
*/
|
||||
private boolean allowOverride = true;
|
||||
|
||||
public boolean isOverrideSystemProperties() {
|
||||
return overrideSystemProperties;
|
||||
}
|
||||
|
||||
public void setOverrideSystemProperties(boolean overrideSystemProperties) {
|
||||
this.overrideSystemProperties = overrideSystemProperties;
|
||||
}
|
||||
|
||||
public boolean isAllowOverride() {
|
||||
return allowOverride;
|
||||
}
|
||||
|
||||
public void setAllowOverride(boolean allowOverride) {
|
||||
this.allowOverride = allowOverride;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2014 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.bootstrap.encrypt;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
|
||||
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.bootstrap.encrypt.KeyProperties.KeyStore;
|
||||
import org.springframework.cloud.config.encrypt.EncryptorFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
import org.springframework.security.crypto.encrypt.TextEncryptor;
|
||||
import org.springframework.security.rsa.crypto.KeyStoreKeyFactory;
|
||||
import org.springframework.security.rsa.crypto.RsaSecretEncryptor;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass({ TextEncryptor.class, RsaSecretEncryptor.class })
|
||||
@EnableConfigurationProperties(KeyProperties.class)
|
||||
public class EncryptionBootstrapConfiguration {
|
||||
|
||||
@Autowired(required = false)
|
||||
private TextEncryptor encryptor;
|
||||
|
||||
@Autowired
|
||||
private KeyProperties key;
|
||||
|
||||
@Configuration
|
||||
@Conditional(KeyCondition.class)
|
||||
@ConditionalOnClass(RsaSecretEncryptor.class)
|
||||
protected static class RsaEncryptionConfiguration {
|
||||
|
||||
@Autowired
|
||||
private KeyProperties key;
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(TextEncryptor.class)
|
||||
public TextEncryptor textEncryptor() {
|
||||
KeyStore keyStore = key.getKeyStore();
|
||||
if (keyStore.getLocation() != null && keyStore.getLocation().exists()) {
|
||||
return new RsaSecretEncryptor(
|
||||
new KeyStoreKeyFactory(keyStore.getLocation(), keyStore
|
||||
.getPassword().toCharArray()).getKeyPair(
|
||||
keyStore.getAlias(), keyStore.getSecret().toCharArray()));
|
||||
}
|
||||
return new EncryptorFactory().create(key.getKey());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Conditional(KeyCondition.class)
|
||||
@ConditionalOnMissingClass(name = "org.springframework.security.rsa.crypto.RsaSecretEncryptor")
|
||||
protected static class VanillaEncryptionConfiguration {
|
||||
|
||||
@Autowired
|
||||
private KeyProperties key;
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(TextEncryptor.class)
|
||||
public TextEncryptor textEncryptor() {
|
||||
return new EncryptorFactory().create(key.getKey());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Bean
|
||||
public EnvironmentDecryptApplicationInitializer environmentDecryptApplicationListener() {
|
||||
if (encryptor == null) {
|
||||
encryptor = new FailsafeTextEncryptor();
|
||||
}
|
||||
EnvironmentDecryptApplicationInitializer listener = new EnvironmentDecryptApplicationInitializer(
|
||||
encryptor);
|
||||
listener.setFailOnError(key.isFailOnError());
|
||||
return listener;
|
||||
}
|
||||
|
||||
public static class KeyCondition extends SpringBootCondition {
|
||||
|
||||
@Override
|
||||
public ConditionOutcome getMatchOutcome(ConditionContext context,
|
||||
AnnotatedTypeMetadata metadata) {
|
||||
Environment environment = context.getEnvironment();
|
||||
if (hasProperty(environment, "encrypt.keyStore.location")) {
|
||||
if (hasProperty(environment, "encrypt.keyStore.password")) {
|
||||
return ConditionOutcome.match("Keystore found in Environment");
|
||||
}
|
||||
return ConditionOutcome
|
||||
.noMatch("Keystore found but no password in Environment");
|
||||
}
|
||||
else if (hasProperty(environment, "encrypt.key")) {
|
||||
return ConditionOutcome.match("Key found in Environment");
|
||||
}
|
||||
return ConditionOutcome.noMatch("Keystore nor key found in Environment");
|
||||
}
|
||||
|
||||
private boolean hasProperty(Environment environment, String key) {
|
||||
String value = environment.getProperty(key);
|
||||
if (value == null) {
|
||||
return false;
|
||||
}
|
||||
return StringUtils.hasText(environment.resolvePlaceholders(value));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* TextEncryptor that just fails, so that users don't get a false sense of security
|
||||
* adding ciphers to config files and not getting them decrypted.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
protected static class FailsafeTextEncryptor implements TextEncryptor {
|
||||
|
||||
@Override
|
||||
public String encrypt(String text) {
|
||||
throw new UnsupportedOperationException(
|
||||
"No encryption for FailsafeTextEncryptor. Did you configure the keystore correctly?");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String decrypt(String encryptedText) {
|
||||
throw new UnsupportedOperationException(
|
||||
"No decryption for FailsafeTextEncryptor. Did you configure the keystore correctly?");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2014 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.bootstrap.encrypt;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.env.CompositePropertySource;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.EnumerablePropertySource;
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.security.crypto.encrypt.TextEncryptor;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class EnvironmentDecryptApplicationInitializer implements
|
||||
ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
|
||||
|
||||
private static Log logger = LogFactory
|
||||
.getLog(EnvironmentDecryptApplicationInitializer.class);
|
||||
|
||||
private int order = Ordered.HIGHEST_PRECEDENCE + 15;
|
||||
|
||||
private TextEncryptor encryptor;
|
||||
|
||||
private boolean failOnError = true;
|
||||
|
||||
public EnvironmentDecryptApplicationInitializer(TextEncryptor encryptor) {
|
||||
this.encryptor = encryptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Strategy to determine how to handle exceptions during decryption.
|
||||
*
|
||||
* @param failOnError the flag value (default true)
|
||||
*/
|
||||
public void setFailOnError(boolean failOnError) {
|
||||
this.failOnError = failOnError;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return order;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(ConfigurableApplicationContext applicationContext) {
|
||||
|
||||
ConfigurableEnvironment environment = applicationContext.getEnvironment();
|
||||
Map<String, Object> overrides = new LinkedHashMap<String, Object>();
|
||||
for (PropertySource<?> source : environment.getPropertySources()) {
|
||||
decrypt(source, overrides);
|
||||
}
|
||||
if (!overrides.isEmpty()) {
|
||||
environment.getPropertySources().addFirst(
|
||||
new MapPropertySource("decrypted", overrides));
|
||||
}
|
||||
}
|
||||
|
||||
private void decrypt(PropertySource<?> source, Map<String, Object> overrides) {
|
||||
|
||||
if (source instanceof EnumerablePropertySource) {
|
||||
|
||||
EnumerablePropertySource<?> enumerable = (EnumerablePropertySource<?>) source;
|
||||
for (String key : enumerable.getPropertyNames()) {
|
||||
String value = source.getProperty(key).toString();
|
||||
if (value.startsWith("{cipher}")) {
|
||||
value = value.substring("{cipher}".length());
|
||||
try {
|
||||
value = encryptor.decrypt(value);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Decrypted: key=" + key);
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
String message = "Cannot decrypt: key=" + key;
|
||||
if (failOnError) {
|
||||
throw new IllegalStateException(message, e);
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.warn(message, e);
|
||||
}
|
||||
else {
|
||||
logger.warn(message);
|
||||
}
|
||||
// Set value to empty to avoid making a password out of the
|
||||
// cipher text
|
||||
value = "";
|
||||
}
|
||||
overrides.put(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else if (source instanceof CompositePropertySource) {
|
||||
|
||||
for (PropertySource<?> nested : ((CompositePropertySource) source)
|
||||
.getPropertySources()) {
|
||||
decrypt(nested, overrides);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2014 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.bootstrap.encrypt;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
@ConfigurationProperties("encrypt")
|
||||
public class KeyProperties {
|
||||
|
||||
private String key;
|
||||
|
||||
private boolean failOnError = true;
|
||||
|
||||
private KeyProperties.KeyStore keyStore = new KeyStore();
|
||||
|
||||
public boolean isFailOnError() {
|
||||
return failOnError;
|
||||
}
|
||||
|
||||
public void setFailOnError(boolean failOnError) {
|
||||
this.failOnError = failOnError;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public void setKey(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public KeyStore getKeyStore() {
|
||||
return keyStore;
|
||||
}
|
||||
|
||||
public void setKeyStore(KeyProperties.KeyStore keyStore) {
|
||||
this.keyStore = keyStore;
|
||||
}
|
||||
|
||||
public static class KeyStore {
|
||||
|
||||
private Resource location;
|
||||
private String password;
|
||||
private String alias;
|
||||
private String secret;
|
||||
|
||||
public String getAlias() {
|
||||
return alias;
|
||||
}
|
||||
|
||||
public void setAlias(String alias) {
|
||||
this.alias = alias;
|
||||
}
|
||||
|
||||
public Resource getLocation() {
|
||||
return location;
|
||||
}
|
||||
|
||||
public void setLocation(Resource location) {
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getSecret() {
|
||||
return secret==null ? password : secret;
|
||||
}
|
||||
|
||||
public void setSecret(String secret) {
|
||||
this.secret = secret;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -13,16 +13,13 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.autoconfigure;
|
||||
package org.springframework.cloud.config.client;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.boot.actuate.health.HealthIndicator;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.cloud.config.client.ConfigClientProperties;
|
||||
import org.springframework.cloud.config.client.ConfigServerHealthIndicator;
|
||||
import org.springframework.cloud.config.client.ConfigServicePropertySourceLocator;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -33,7 +30,7 @@ import org.springframework.core.env.Environment;
|
||||
* bound to it. It won't be available in time for autowiring into the bootstrap context,
|
||||
* but the values in this properties object will be the same as the ones used to bind to
|
||||
* the config server, if there is one.
|
||||
*
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@@ -43,9 +40,11 @@ public class ConfigClientAutoConfiguration {
|
||||
@Bean
|
||||
public ConfigClientProperties configClientProperties(Environment environment,
|
||||
ApplicationContext context) {
|
||||
if (context.getParent()!=null && BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context.getParent(),
|
||||
ConfigClientProperties.class).length > 0) {
|
||||
return BeanFactoryUtils.beanOfType(context.getParent(), ConfigClientProperties.class);
|
||||
if (context.getParent() != null
|
||||
&& BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
|
||||
context.getParent(), ConfigClientProperties.class).length > 0) {
|
||||
return BeanFactoryUtils.beanOfType(context.getParent(),
|
||||
ConfigClientProperties.class);
|
||||
}
|
||||
ConfigClientProperties client = new ConfigClientProperties(environment);
|
||||
return client;
|
||||
@@ -57,10 +56,11 @@ public class ConfigClientAutoConfiguration {
|
||||
@ConditionalOnProperty(value = "spring.cloud.config.enabled", matchIfMissing = true)
|
||||
protected static class ConfigServerHealthIndicatorConfiguration {
|
||||
|
||||
@Bean
|
||||
public ConfigServerHealthIndicator configServerHealthIndicator(ConfigServicePropertySourceLocator locator) {
|
||||
return new ConfigServerHealthIndicator(locator);
|
||||
}
|
||||
@Bean
|
||||
public ConfigServerHealthIndicator configServerHealthIndicator(
|
||||
ConfigServicePropertySourceLocator locator) {
|
||||
return new ConfigServerHealthIndicator(locator);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2013-2014 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.config.client;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties
|
||||
public class ConfigServiceBootstrapConfiguration {
|
||||
|
||||
@Autowired
|
||||
private ConfigurableEnvironment environment;
|
||||
|
||||
@Bean
|
||||
public ConfigClientProperties configClientProperties() {
|
||||
ConfigClientProperties client = new ConfigClientProperties(this.environment);
|
||||
return client;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(value = "spring.cloud.config.enabled", matchIfMissing = true)
|
||||
public ConfigServicePropertySourceLocator configServicePropertySource() {
|
||||
ConfigServicePropertySourceLocator locator = new ConfigServicePropertySourceLocator(
|
||||
configClientProperties());
|
||||
return locator;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -22,6 +22,7 @@ import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.cloud.bootstrap.config.PropertySourceLocator;
|
||||
import org.springframework.cloud.config.environment.Environment;
|
||||
import org.springframework.cloud.config.environment.PropertySource;
|
||||
import org.springframework.core.annotation.Order;
|
||||
@@ -34,7 +35,6 @@ import org.springframework.http.MediaType;
|
||||
import org.springframework.http.client.ClientHttpRequestExecution;
|
||||
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.security.crypto.codec.Base64;
|
||||
import org.springframework.web.client.HttpServerErrorException;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
@@ -136,3 +136,634 @@ public class ConfigServicePropertySourceLocator implements PropertySourceLocator
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Base64 encoder which is a reduced version of Robert Harder's public domain
|
||||
* implementation (version 2.3.7). See <a
|
||||
* href="http://iharder.net/base64">http://iharder.net/base64</a> for more information.
|
||||
* <p>
|
||||
* For internal use only.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @since 3.0
|
||||
*/
|
||||
final class Base64 {
|
||||
|
||||
/** No options specified. Value is zero. */
|
||||
public final static int NO_OPTIONS = 0;
|
||||
|
||||
/** Specify encoding in first bit. Value is one. */
|
||||
public final static int ENCODE = 1;
|
||||
|
||||
/** Specify decoding in first bit. Value is zero. */
|
||||
public final static int DECODE = 0;
|
||||
|
||||
/** Do break lines when encoding. Value is 8. */
|
||||
public final static int DO_BREAK_LINES = 8;
|
||||
|
||||
/**
|
||||
* Encode using Base64-like encoding that is URL- and Filename-safe as described in
|
||||
* Section 4 of RFC3548: <a
|
||||
* href="http://www.faqs.org/rfcs/rfc3548.html">http://www.faqs
|
||||
* .org/rfcs/rfc3548.html</a>. It is important to note that data encoded this way is
|
||||
* <em>not</em> officially valid Base64, or at the very least should not be called
|
||||
* Base64 without also specifying that is was encoded using the URL- and Filename-safe
|
||||
* dialect.
|
||||
*/
|
||||
public final static int URL_SAFE = 16;
|
||||
|
||||
/**
|
||||
* Encode using the special "ordered" dialect of Base64 described here: <a
|
||||
* href="http://www.faqs.org/qa/rfcc-1940.html"
|
||||
* >http://www.faqs.org/qa/rfcc-1940.html</a>.
|
||||
*/
|
||||
public final static int ORDERED = 32;
|
||||
|
||||
/** Maximum line length (76) of Base64 output. */
|
||||
private final static int MAX_LINE_LENGTH = 76;
|
||||
|
||||
/** The equals sign (=) as a byte. */
|
||||
private final static byte EQUALS_SIGN = (byte) '=';
|
||||
|
||||
/** The new line character (\n) as a byte. */
|
||||
private final static byte NEW_LINE = (byte) '\n';
|
||||
|
||||
private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding
|
||||
private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding
|
||||
|
||||
/* ******** S T A N D A R D B A S E 6 4 A L P H A B E T ******** */
|
||||
|
||||
/** The 64 valid Base64 values. */
|
||||
/* Host platform me be something funny like EBCDIC, so we hardcode these values. */
|
||||
private final static byte[] _STANDARD_ALPHABET = { (byte) 'A', (byte) 'B',
|
||||
(byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F', (byte) 'G', (byte) 'H',
|
||||
(byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N',
|
||||
(byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T',
|
||||
(byte) 'U', (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z',
|
||||
(byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f',
|
||||
(byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l',
|
||||
(byte) 'm', (byte) 'n', (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r',
|
||||
(byte) 's', (byte) 't', (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x',
|
||||
(byte) 'y', (byte) 'z', (byte) '0', (byte) '1', (byte) '2', (byte) '3',
|
||||
(byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9',
|
||||
(byte) '+', (byte) '/' };
|
||||
|
||||
/**
|
||||
* Translates a Base64 value to either its 6-bit reconstruction value or a negative
|
||||
* number indicating some other meaning.
|
||||
**/
|
||||
private final static byte[] _STANDARD_DECODABET = { -9, -9, -9, -9, -9, -9, -9, -9,
|
||||
-9, // Decimal 0 - 8
|
||||
-5, -5, // Whitespace: Tab and Linefeed
|
||||
-9, -9, // Decimal 11 - 12
|
||||
-5, // Whitespace: Carriage Return
|
||||
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26
|
||||
-9, -9, -9, -9, -9, // Decimal 27 - 31
|
||||
-5, // Whitespace: Space
|
||||
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42
|
||||
62, // Plus sign at decimal 43
|
||||
-9, -9, -9, // Decimal 44 - 46
|
||||
63, // Slash at decimal 47
|
||||
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine
|
||||
-9, -9, -9, // Decimal 58 - 60
|
||||
-1, // Equals sign at decimal 61
|
||||
-9, -9, -9, // Decimal 62 - 64
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through 'N'
|
||||
14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O' through 'Z'
|
||||
-9, -9, -9, -9, -9, -9, // Decimal 91 - 96
|
||||
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a' through 'm'
|
||||
39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n' through 'z'
|
||||
-9, -9, -9, -9, -9 // Decimal 123 - 127
|
||||
, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 128 - 139
|
||||
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 140 - 152
|
||||
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 153 - 165
|
||||
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 166 - 178
|
||||
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 179 - 191
|
||||
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 192 - 204
|
||||
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 205 - 217
|
||||
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 218 - 230
|
||||
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 231 - 243
|
||||
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9 // Decimal 244 - 255
|
||||
};
|
||||
|
||||
/* ******** U R L S A F E B A S E 6 4 A L P H A B E T ******** */
|
||||
|
||||
/**
|
||||
* Used in the URL- and Filename-safe dialect described in Section 4 of RFC3548: <a
|
||||
* href
|
||||
* ="http://www.faqs.org/rfcs/rfc3548.html">http://www.faqs.org/rfcs/rfc3548.html</a>.
|
||||
* Notice that the last two bytes become "hyphen" and "underscore" instead of "plus"
|
||||
* and "slash."
|
||||
*/
|
||||
private final static byte[] _URL_SAFE_ALPHABET = { (byte) 'A', (byte) 'B',
|
||||
(byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F', (byte) 'G', (byte) 'H',
|
||||
(byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N',
|
||||
(byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T',
|
||||
(byte) 'U', (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z',
|
||||
(byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f',
|
||||
(byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l',
|
||||
(byte) 'm', (byte) 'n', (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r',
|
||||
(byte) 's', (byte) 't', (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x',
|
||||
(byte) 'y', (byte) 'z', (byte) '0', (byte) '1', (byte) '2', (byte) '3',
|
||||
(byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9',
|
||||
(byte) '-', (byte) '_' };
|
||||
|
||||
/**
|
||||
* Used in decoding URL- and Filename-safe dialects of Base64.
|
||||
*/
|
||||
private final static byte[] _URL_SAFE_DECODABET = { -9, -9, -9, -9, -9, -9, -9, -9,
|
||||
-9, // Decimal 0 - 8
|
||||
-5, -5, // Whitespace: Tab and Linefeed
|
||||
-9, -9, // Decimal 11 - 12
|
||||
-5, // Whitespace: Carriage Return
|
||||
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26
|
||||
-9, -9, -9, -9, -9, // Decimal 27 - 31
|
||||
-5, // Whitespace: Space
|
||||
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42
|
||||
-9, // Plus sign at decimal 43
|
||||
-9, // Decimal 44
|
||||
62, // Minus sign at decimal 45
|
||||
-9, // Decimal 46
|
||||
-9, // Slash at decimal 47
|
||||
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine
|
||||
-9, -9, -9, // Decimal 58 - 60
|
||||
-1, // Equals sign at decimal 61
|
||||
-9, -9, -9, // Decimal 62 - 64
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through 'N'
|
||||
14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O' through 'Z'
|
||||
-9, -9, -9, -9, // Decimal 91 - 94
|
||||
63, // Underscore at decimal 95
|
||||
-9, // Decimal 96
|
||||
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a' through 'm'
|
||||
39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n' through 'z'
|
||||
-9, -9, -9, -9, -9 // Decimal 123 - 127
|
||||
, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 128 - 139
|
||||
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 140 - 152
|
||||
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 153 - 165
|
||||
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 166 - 178
|
||||
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 179 - 191
|
||||
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 192 - 204
|
||||
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 205 - 217
|
||||
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 218 - 230
|
||||
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 231 - 243
|
||||
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9 // Decimal 244 - 255
|
||||
};
|
||||
|
||||
/* ******** O R D E R E D B A S E 6 4 A L P H A B E T ******** */
|
||||
|
||||
/**
|
||||
* I don't get the point of this technique, but someone requested it, and it is
|
||||
* described here: <a
|
||||
* href="http://www.faqs.org/qa/rfcc-1940.html">http://www.faqs.org/
|
||||
* qa/rfcc-1940.html</a>.
|
||||
*/
|
||||
private final static byte[] _ORDERED_ALPHABET = { (byte) '-', (byte) '0', (byte) '1',
|
||||
(byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7',
|
||||
(byte) '8', (byte) '9', (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D',
|
||||
(byte) 'E', (byte) 'F', (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J',
|
||||
(byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P',
|
||||
(byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U', (byte) 'V',
|
||||
(byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z', (byte) '_', (byte) 'a',
|
||||
(byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f', (byte) 'g',
|
||||
(byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l', (byte) 'm',
|
||||
(byte) 'n', (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's',
|
||||
(byte) 't', (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y',
|
||||
(byte) 'z' };
|
||||
|
||||
/**
|
||||
* Used in decoding the "ordered" dialect of Base64.
|
||||
*/
|
||||
private final static byte[] _ORDERED_DECODABET = { -9, -9, -9, -9, -9, -9, -9, -9,
|
||||
-9, // Decimal 0 - 8
|
||||
-5, -5, // Whitespace: Tab and Linefeed
|
||||
-9, -9, // Decimal 11 - 12
|
||||
-5, // Whitespace: Carriage Return
|
||||
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26
|
||||
-9, -9, -9, -9, -9, // Decimal 27 - 31
|
||||
-5, // Whitespace: Space
|
||||
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42
|
||||
-9, // Plus sign at decimal 43
|
||||
-9, // Decimal 44
|
||||
0, // Minus sign at decimal 45
|
||||
-9, // Decimal 46
|
||||
-9, // Slash at decimal 47
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, // Numbers zero through nine
|
||||
-9, -9, -9, // Decimal 58 - 60
|
||||
-1, // Equals sign at decimal 61
|
||||
-9, -9, -9, // Decimal 62 - 64
|
||||
11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, // Letters 'A' through 'M'
|
||||
24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, // Letters 'N' through 'Z'
|
||||
-9, -9, -9, -9, // Decimal 91 - 94
|
||||
37, // Underscore at decimal 95
|
||||
-9, // Decimal 96
|
||||
38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, // Letters 'a' through 'm'
|
||||
51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, // Letters 'n' through 'z'
|
||||
-9, -9, -9, -9, -9 // Decimal 123 - 127
|
||||
, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 128 - 139
|
||||
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 140 - 152
|
||||
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 153 - 165
|
||||
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 166 - 178
|
||||
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 179 - 191
|
||||
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 192 - 204
|
||||
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 205 - 217
|
||||
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 218 - 230
|
||||
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 231 - 243
|
||||
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9 // Decimal 244 - 255
|
||||
};
|
||||
|
||||
public static byte[] decode(byte[] bytes) {
|
||||
return decode(bytes, 0, bytes.length, NO_OPTIONS);
|
||||
}
|
||||
|
||||
public static byte[] encode(byte[] bytes) {
|
||||
return encodeBytesToBytes(bytes, 0, bytes.length, NO_OPTIONS);
|
||||
}
|
||||
|
||||
public static boolean isBase64(byte[] bytes) {
|
||||
try {
|
||||
decode(bytes);
|
||||
}
|
||||
catch (InvalidBase64CharacterException e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns one of the _SOMETHING_ALPHABET byte arrays depending on the options
|
||||
* specified. It's possible, though silly, to specify ORDERED <b>and</b> URLSAFE in
|
||||
* which case one of them will be picked, though there is no guarantee as to which one
|
||||
* will be picked.
|
||||
*/
|
||||
private static byte[] getAlphabet(int options) {
|
||||
if ((options & URL_SAFE) == URL_SAFE) {
|
||||
return _URL_SAFE_ALPHABET;
|
||||
}
|
||||
else if ((options & ORDERED) == ORDERED) {
|
||||
return _ORDERED_ALPHABET;
|
||||
}
|
||||
else {
|
||||
return _STANDARD_ALPHABET;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns one of the _SOMETHING_DECODABET byte arrays depending on the options
|
||||
* specified. It's possible, though silly, to specify ORDERED and URL_SAFE in which
|
||||
* case one of them will be picked, though there is no guarantee as to which one will
|
||||
* be picked.
|
||||
*/
|
||||
private static byte[] getDecodabet(int options) {
|
||||
if ((options & URL_SAFE) == URL_SAFE) {
|
||||
return _URL_SAFE_DECODABET;
|
||||
}
|
||||
else if ((options & ORDERED) == ORDERED) {
|
||||
return _ORDERED_DECODABET;
|
||||
}
|
||||
else {
|
||||
return _STANDARD_DECODABET;
|
||||
}
|
||||
}
|
||||
|
||||
/* ******** E N C O D I N G M E T H O D S ******** */
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Encodes up to three bytes of the array <var>source</var> and writes the resulting
|
||||
* four Base64 bytes to <var>destination</var>. The source and destination arrays can
|
||||
* be manipulated anywhere along their length by specifying <var>srcOffset</var> and
|
||||
* <var>destOffset</var>. This method does not check to make sure your arrays are
|
||||
* large enough to accomodate <var>srcOffset</var> + 3 for the <var>source</var> array
|
||||
* or <var>destOffset</var> + 4 for the <var>destination</var> array. The actual
|
||||
* number of significant bytes in your array is given by <var>numSigBytes</var>.
|
||||
* </p>
|
||||
* <p>
|
||||
* This is the lowest level of the encoding methods with all possible parameters.
|
||||
* </p>
|
||||
*
|
||||
* @param source the array to convert
|
||||
* @param srcOffset the index where conversion begins
|
||||
* @param numSigBytes the number of significant bytes in your array
|
||||
* @param destination the array to hold the conversion
|
||||
* @param destOffset the index where output will be put
|
||||
* @return the <var>destination</var> array
|
||||
* @since 1.3
|
||||
*/
|
||||
private static byte[] encode3to4(byte[] source, int srcOffset, int numSigBytes,
|
||||
byte[] destination, int destOffset, int options) {
|
||||
|
||||
byte[] ALPHABET = getAlphabet(options);
|
||||
|
||||
// 1 2 3
|
||||
// 01234567890123456789012345678901 Bit position
|
||||
// --------000000001111111122222222 Array position from threeBytes
|
||||
// --------| || || || | Six bit groups to index ALPHABET
|
||||
// >>18 >>12 >> 6 >> 0 Right shift necessary
|
||||
// 0x3f 0x3f 0x3f Additional AND
|
||||
|
||||
// Create buffer with zero-padding if there are only one or two
|
||||
// significant bytes passed in the array.
|
||||
// We have to shift left 24 in order to flush out the 1's that appear
|
||||
// when Java treats a value as negative that is cast from a byte to an int.
|
||||
int inBuff = (numSigBytes > 0 ? ((source[srcOffset] << 24) >>> 8) : 0)
|
||||
| (numSigBytes > 1 ? ((source[srcOffset + 1] << 24) >>> 16) : 0)
|
||||
| (numSigBytes > 2 ? ((source[srcOffset + 2] << 24) >>> 24) : 0);
|
||||
|
||||
switch (numSigBytes) {
|
||||
case 3:
|
||||
destination[destOffset] = ALPHABET[(inBuff >>> 18)];
|
||||
destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
|
||||
destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f];
|
||||
destination[destOffset + 3] = ALPHABET[(inBuff) & 0x3f];
|
||||
return destination;
|
||||
|
||||
case 2:
|
||||
destination[destOffset] = ALPHABET[(inBuff >>> 18)];
|
||||
destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
|
||||
destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f];
|
||||
destination[destOffset + 3] = EQUALS_SIGN;
|
||||
return destination;
|
||||
|
||||
case 1:
|
||||
destination[destOffset] = ALPHABET[(inBuff >>> 18)];
|
||||
destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
|
||||
destination[destOffset + 2] = EQUALS_SIGN;
|
||||
destination[destOffset + 3] = EQUALS_SIGN;
|
||||
return destination;
|
||||
|
||||
default:
|
||||
return destination;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param source The data to convert
|
||||
* @param off Offset in array where conversion should begin
|
||||
* @param len Length of data to convert
|
||||
* @param options Specified options
|
||||
* @return The Base64-encoded data as a String
|
||||
* @see Base64#DO_BREAK_LINES
|
||||
* @throws java.io.IOException if there is an error
|
||||
* @throws NullPointerException if source array is null
|
||||
* @throws IllegalArgumentException if source array, offset, or length are invalid
|
||||
* @since 2.3.1
|
||||
*/
|
||||
private static byte[] encodeBytesToBytes(byte[] source, int off, int len, int options) {
|
||||
|
||||
if (source == null) {
|
||||
throw new NullPointerException("Cannot serialize a null array.");
|
||||
} // end if: null
|
||||
|
||||
if (off < 0) {
|
||||
throw new IllegalArgumentException("Cannot have negative offset: " + off);
|
||||
} // end if: off < 0
|
||||
|
||||
if (len < 0) {
|
||||
throw new IllegalArgumentException("Cannot have length offset: " + len);
|
||||
} // end if: len < 0
|
||||
|
||||
if (off + len > source.length) {
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"Cannot have offset of %d and length of %d with array of length %d",
|
||||
off, len, source.length));
|
||||
} // end if: off < 0
|
||||
|
||||
boolean breakLines = (options & DO_BREAK_LINES) > 0;
|
||||
|
||||
// int len43 = len * 4 / 3;
|
||||
// byte[] outBuff = new byte[ ( len43 ) // Main 4:3
|
||||
// + ( (len % 3) > 0 ? 4 : 0 ) // Account for padding
|
||||
// + (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines
|
||||
// Try to determine more precisely how big the array needs to be.
|
||||
// If we get it right, we don't have to do an array copy, and
|
||||
// we save a bunch of memory.
|
||||
int encLen = (len / 3) * 4 + (len % 3 > 0 ? 4 : 0); // Bytes needed for actual
|
||||
// encoding
|
||||
if (breakLines) {
|
||||
encLen += encLen / MAX_LINE_LENGTH; // Plus extra newline characters
|
||||
}
|
||||
byte[] outBuff = new byte[encLen];
|
||||
|
||||
int d = 0;
|
||||
int e = 0;
|
||||
int len2 = len - 2;
|
||||
int lineLength = 0;
|
||||
for (; d < len2; d += 3, e += 4) {
|
||||
encode3to4(source, d + off, 3, outBuff, e, options);
|
||||
|
||||
lineLength += 4;
|
||||
if (breakLines && lineLength >= MAX_LINE_LENGTH) {
|
||||
outBuff[e + 4] = NEW_LINE;
|
||||
e++;
|
||||
lineLength = 0;
|
||||
} // end if: end of line
|
||||
} // en dfor: each piece of array
|
||||
|
||||
if (d < len) {
|
||||
encode3to4(source, d + off, len - d, outBuff, e, options);
|
||||
e += 4;
|
||||
} // end if: some padding needed
|
||||
|
||||
// Only resize array if we didn't guess it right.
|
||||
if (e <= outBuff.length - 1) {
|
||||
byte[] finalOut = new byte[e];
|
||||
System.arraycopy(outBuff, 0, finalOut, 0, e);
|
||||
// System.err.println("Having to resize array from " + outBuff.length + " to "
|
||||
// + e );
|
||||
return finalOut;
|
||||
}
|
||||
else {
|
||||
// System.err.println("No need to resize array.");
|
||||
return outBuff;
|
||||
}
|
||||
}
|
||||
|
||||
/* ******** D E C O D I N G M E T H O D S ******** */
|
||||
|
||||
/**
|
||||
* Decodes four bytes from array <var>source</var> and writes the resulting bytes (up
|
||||
* to three of them) to <var>destination</var>. The source and destination arrays can
|
||||
* be manipulated anywhere along their length by specifying <var>srcOffset</var> and
|
||||
* <var>destOffset</var>. This method does not check to make sure your arrays are
|
||||
* large enough to accomodate <var>srcOffset</var> + 4 for the <var>source</var> array
|
||||
* or <var>destOffset</var> + 3 for the <var>destination</var> array. This method
|
||||
* returns the actual number of bytes that were converted from the Base64 encoding.
|
||||
* <p>
|
||||
* This is the lowest level of the decoding methods with all possible parameters.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @param source the array to convert
|
||||
* @param srcOffset the index where conversion begins
|
||||
* @param destination the array to hold the conversion
|
||||
* @param destOffset the index where output will be put
|
||||
* @param options alphabet type is pulled from this (standard, url-safe, ordered)
|
||||
* @return the number of decoded bytes converted
|
||||
* @throws NullPointerException if source or destination arrays are null
|
||||
* @throws IllegalArgumentException if srcOffset or destOffset are invalid or there is
|
||||
* not enough room in the array.
|
||||
* @since 1.3
|
||||
*/
|
||||
private static int decode4to3(final byte[] source, final int srcOffset,
|
||||
final byte[] destination, final int destOffset, final int options) {
|
||||
|
||||
// Lots of error checking and exception throwing
|
||||
if (source == null) {
|
||||
throw new NullPointerException("Source array was null.");
|
||||
} // end if
|
||||
if (destination == null) {
|
||||
throw new NullPointerException("Destination array was null.");
|
||||
} // end if
|
||||
if (srcOffset < 0 || srcOffset + 3 >= source.length) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format(
|
||||
"Source array with length %d cannot have offset of %d and still process four bytes.",
|
||||
source.length, srcOffset));
|
||||
} // end if
|
||||
if (destOffset < 0 || destOffset + 2 >= destination.length) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format(
|
||||
"Destination array with length %d cannot have offset of %d and still store three bytes.",
|
||||
destination.length, destOffset));
|
||||
} // end if
|
||||
|
||||
byte[] DECODABET = getDecodabet(options);
|
||||
|
||||
// Example: Dk==
|
||||
if (source[srcOffset + 2] == EQUALS_SIGN) {
|
||||
// Two ways to do the same thing. Don't know which way I like best.
|
||||
// int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
|
||||
// | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 );
|
||||
int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18)
|
||||
| ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12);
|
||||
|
||||
destination[destOffset] = (byte) (outBuff >>> 16);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Example: DkL=
|
||||
else if (source[srcOffset + 3] == EQUALS_SIGN) {
|
||||
// Two ways to do the same thing. Don't know which way I like best.
|
||||
// int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
|
||||
// | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
|
||||
// | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 );
|
||||
int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18)
|
||||
| ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12)
|
||||
| ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6);
|
||||
|
||||
destination[destOffset] = (byte) (outBuff >>> 16);
|
||||
destination[destOffset + 1] = (byte) (outBuff >>> 8);
|
||||
return 2;
|
||||
}
|
||||
|
||||
// Example: DkLE
|
||||
else {
|
||||
// Two ways to do the same thing. Don't know which way I like best.
|
||||
// int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
|
||||
// | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
|
||||
// | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 )
|
||||
// | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 );
|
||||
int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18)
|
||||
| ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12)
|
||||
| ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6)
|
||||
| ((DECODABET[source[srcOffset + 3]] & 0xFF));
|
||||
|
||||
destination[destOffset] = (byte) (outBuff >> 16);
|
||||
destination[destOffset + 1] = (byte) (outBuff >> 8);
|
||||
destination[destOffset + 2] = (byte) (outBuff);
|
||||
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Low-level access to decoding ASCII characters in the form of a byte array.
|
||||
* <strong>Ignores GUNZIP option, if it's set.</strong> This is not generally a
|
||||
* recommended method, although it is used internally as part of the decoding process.
|
||||
* Special case: if len = 0, an empty array is returned. Still, if you need more speed
|
||||
* and reduced memory footprint (and aren't gzipping), consider this method.
|
||||
*
|
||||
* @param source The Base64 encoded data
|
||||
* @param off The offset of where to begin decoding
|
||||
* @param len The length of characters to decode
|
||||
* @param options Can specify options such as alphabet type to use
|
||||
* @return decoded data
|
||||
* @throws IllegalArgumentException If bogus characters exist in source data
|
||||
*/
|
||||
private static byte[] decode(final byte[] source, final int off, final int len,
|
||||
final int options) {
|
||||
|
||||
// Lots of error checking and exception throwing
|
||||
if (source == null) {
|
||||
throw new NullPointerException("Cannot decode null source array.");
|
||||
} // end if
|
||||
if (off < 0 || off + len > source.length) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format(
|
||||
"Source array with length %d cannot have offset of %d and process %d bytes.",
|
||||
source.length, off, len));
|
||||
} // end if
|
||||
|
||||
if (len == 0) {
|
||||
return new byte[0];
|
||||
}
|
||||
else if (len < 4) {
|
||||
throw new IllegalArgumentException(
|
||||
"Base64-encoded string must have at least four characters, but length specified was "
|
||||
+ len);
|
||||
} // end if
|
||||
|
||||
byte[] DECODABET = getDecodabet(options);
|
||||
|
||||
int len34 = len * 3 / 4; // Estimate on array size
|
||||
byte[] outBuff = new byte[len34]; // Upper limit on size of output
|
||||
int outBuffPosn = 0; // Keep track of where we're writing
|
||||
|
||||
byte[] b4 = new byte[4]; // Four byte buffer from source, eliminating white space
|
||||
int b4Posn = 0; // Keep track of four byte input buffer
|
||||
int i = 0; // Source array counter
|
||||
byte sbiDecode = 0; // Special value from DECODABET
|
||||
|
||||
for (i = off; i < off + len; i++) { // Loop through source
|
||||
|
||||
sbiDecode = DECODABET[source[i] & 0xFF];
|
||||
|
||||
// White space, Equals sign, or legit Base64 character
|
||||
// Note the values such as -5 and -9 in the
|
||||
// DECODABETs at the top of the file.
|
||||
if (sbiDecode >= WHITE_SPACE_ENC) {
|
||||
if (sbiDecode >= EQUALS_SIGN_ENC) {
|
||||
b4[b4Posn++] = source[i]; // Save non-whitespace
|
||||
if (b4Posn > 3) { // Time to decode?
|
||||
outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn, options);
|
||||
b4Posn = 0;
|
||||
|
||||
// If that was the equals sign, break out of 'for' loop
|
||||
if (source[i] == EQUALS_SIGN) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// There's a bad input character in the Base64 stream.
|
||||
throw new InvalidBase64CharacterException(String.format(
|
||||
"Bad Base64 input character decimal %d in array position %d",
|
||||
((int) source[i]) & 0xFF, i));
|
||||
}
|
||||
}
|
||||
|
||||
byte[] out = new byte[outBuffPosn];
|
||||
System.arraycopy(outBuff, 0, out, 0, outBuffPosn);
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
class InvalidBase64CharacterException extends IllegalArgumentException {
|
||||
|
||||
InvalidBase64CharacterException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2014 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.config.client;
|
||||
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
|
||||
/**
|
||||
* Strategy for locating (possibly remote) property sources for the Environment.
|
||||
* Implementations should not fail unless they intend to prevent the application from
|
||||
* starting.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public interface PropertySourceLocator {
|
||||
|
||||
/**
|
||||
* @param environment the current Environment
|
||||
* @return a PropertySource or null if there is none
|
||||
*
|
||||
* @throws IllegalStateException if there is a fail fast condition
|
||||
*/
|
||||
PropertySource<?> locate(Environment environment);
|
||||
|
||||
}
|
||||
@@ -1,165 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2014 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.config.client;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.AbstractEndpoint;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
|
||||
import org.springframework.cloud.context.scope.refresh.RefreshScope;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.CompositePropertySource;
|
||||
import org.springframework.core.env.EnumerablePropertySource;
|
||||
import org.springframework.core.env.MutablePropertySources;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
import org.springframework.jmx.export.annotation.ManagedOperation;
|
||||
import org.springframework.jmx.export.annotation.ManagedResource;
|
||||
import org.springframework.web.context.support.StandardServletEnvironment;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "endpoints.refresh", ignoreUnknownFields = false)
|
||||
@ManagedResource
|
||||
public class RefreshEndpoint extends AbstractEndpoint<Collection<String>> {
|
||||
|
||||
private Set<String> standardSources = new HashSet<String>(Arrays.asList(
|
||||
StandardEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME,
|
||||
StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME,
|
||||
StandardServletEnvironment.JNDI_PROPERTY_SOURCE_NAME,
|
||||
StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME,
|
||||
StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME));
|
||||
|
||||
private ConfigurableApplicationContext context;
|
||||
|
||||
private RefreshScope scope;
|
||||
|
||||
public RefreshEndpoint(ConfigurableApplicationContext context, RefreshScope scope) {
|
||||
super("refresh");
|
||||
this.context = context;
|
||||
this.scope = scope;
|
||||
}
|
||||
|
||||
@ManagedOperation
|
||||
public synchronized String[] refresh() {
|
||||
Map<String, Object> before = extract(context.getEnvironment().getPropertySources());
|
||||
addConfigFilesToEnvironment();
|
||||
Set<String> keys = changes(before,
|
||||
extract(context.getEnvironment().getPropertySources())).keySet();
|
||||
scope.refreshAll();
|
||||
if (keys.isEmpty()) {
|
||||
return new String[0];
|
||||
}
|
||||
context.publishEvent(new EnvironmentChangeEvent(keys));
|
||||
return keys.toArray(new String[keys.size()]);
|
||||
}
|
||||
|
||||
private void addConfigFilesToEnvironment() {
|
||||
ConfigurableApplicationContext capture = new SpringApplicationBuilder(Empty.class).showBanner(
|
||||
false).web(false).environment(context.getEnvironment()).run();
|
||||
MutablePropertySources target = context.getEnvironment().getPropertySources();
|
||||
for (PropertySource<?> source : capture.getEnvironment().getPropertySources()) {
|
||||
String name = source.getName();
|
||||
if (!standardSources.contains(name)) {
|
||||
if (target.contains(name)) {
|
||||
target.replace(name, source);
|
||||
} else {
|
||||
if (target.contains("defaultProperties")) {
|
||||
target.addBefore("defaultProperties", source);
|
||||
} else {
|
||||
target.addLast(source);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> invoke() {
|
||||
return Arrays.asList(refresh());
|
||||
}
|
||||
|
||||
private Map<String, Object> changes(Map<String, Object> before,
|
||||
Map<String, Object> after) {
|
||||
Map<String, Object> result = new HashMap<String, Object>();
|
||||
for (String key : before.keySet()) {
|
||||
if (!after.containsKey(key)) {
|
||||
result.put(key, null);
|
||||
} else if (!equal(before.get(key), after.get(key))) {
|
||||
result.put(key, after.get(key));
|
||||
}
|
||||
}
|
||||
for (String key : after.keySet()) {
|
||||
if (!before.containsKey(key)) {
|
||||
result.put(key, after.get(key));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean equal(Object one, Object two) {
|
||||
if (one == null && two == null) {
|
||||
return true;
|
||||
}
|
||||
if (one == null || two == null) {
|
||||
return false;
|
||||
}
|
||||
return one.equals(two);
|
||||
}
|
||||
|
||||
private Map<String, Object> extract(MutablePropertySources propertySources) {
|
||||
Map<String, Object> result = new HashMap<String, Object>();
|
||||
for (PropertySource<?> parent : propertySources) {
|
||||
if (!standardSources.contains(parent.getName())) {
|
||||
extract(parent, result);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void extract(PropertySource<?> parent, Map<String, Object> result) {
|
||||
if (parent instanceof CompositePropertySource) {
|
||||
try {
|
||||
for (PropertySource<?> source : ((CompositePropertySource) parent).getPropertySources()) {
|
||||
extract(source, result);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return;
|
||||
}
|
||||
} else if (parent instanceof EnumerablePropertySource) {
|
||||
for (String key : ((EnumerablePropertySource<?>) parent).getPropertyNames()) {
|
||||
result.put(key, parent.getProperty(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
protected static class Empty {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2014 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.config.encrypt;
|
||||
|
||||
import org.springframework.security.crypto.encrypt.Encryptors;
|
||||
import org.springframework.security.crypto.encrypt.TextEncryptor;
|
||||
import org.springframework.security.rsa.crypto.RsaSecretEncryptor;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class EncryptorFactory {
|
||||
|
||||
// TODO: expose as config property
|
||||
private static final String SALT = "deadbeef";
|
||||
|
||||
public TextEncryptor create(String data) {
|
||||
|
||||
TextEncryptor encryptor;
|
||||
if (data.contains("RSA PRIVATE KEY")) {
|
||||
|
||||
try {
|
||||
encryptor = new RsaSecretEncryptor(data);
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
throw new KeyFormatException();
|
||||
}
|
||||
|
||||
}
|
||||
else if (data.startsWith("ssh-rsa") || data.contains("RSA PUBLIC KEY")) {
|
||||
throw new KeyFormatException();
|
||||
}
|
||||
else {
|
||||
encryptor = Encryptors.text(data, SALT);
|
||||
}
|
||||
|
||||
return encryptor;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2014 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.config.encrypt;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class KeyFormatException extends RuntimeException {
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.context.config;
|
||||
|
||||
|
||||
/**
|
||||
* A helper interface providing optional decoration of bean instances and their
|
||||
* destruction callbacks. Users can supply custom implementations of this
|
||||
* strategy if they want tighter control over method invocation on the bean or
|
||||
* its destruction callback.
|
||||
*
|
||||
* @param <T>
|
||||
* the type of auxiliary context object that can be passed between
|
||||
* methods. Implementations can choose what type of data to supply as
|
||||
* it is passed around unchanged by the caller.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public interface BeanLifecycleDecorator<T> {
|
||||
|
||||
/**
|
||||
* Optionally decorate and provide a new instance of a compatible bean for
|
||||
* the caller to use instead of the input.
|
||||
*
|
||||
* @param bean
|
||||
* the bean to optionally decorate
|
||||
* @param context
|
||||
* the context as created by
|
||||
* {@link #decorateDestructionCallback(Runnable)}
|
||||
* @return the replacement bean for the caller to use
|
||||
*/
|
||||
Object decorateBean(Object bean, Context<T> context);
|
||||
|
||||
/**
|
||||
* Optionally decorate the destruction callback provided, and also return
|
||||
* some context that can be used later by the
|
||||
* {@link #decorateBean(Object, Context)} method.
|
||||
*
|
||||
* @param callback
|
||||
* the destruction callback that will be used by the container
|
||||
* @return a context wrapper
|
||||
*/
|
||||
Context<T> decorateDestructionCallback(Runnable callback);
|
||||
|
||||
static class Context<T> {
|
||||
|
||||
private final T auxiliary;
|
||||
private final Runnable callback;
|
||||
|
||||
public Context(Runnable callback, T auxiliary) {
|
||||
this.callback = callback;
|
||||
this.auxiliary = auxiliary;
|
||||
}
|
||||
|
||||
public Runnable getCallback() {
|
||||
return callback;
|
||||
}
|
||||
|
||||
public T getAuxiliary() {
|
||||
return auxiliary;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.context.config;
|
||||
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
|
||||
/**
|
||||
* A {@link BeanLifecycleDecorator} that tries to protect against concurrent access to a bean during its own destruction.
|
||||
* A read-write lock is used, and method access is protected using the read lock, while the destruction callback is
|
||||
* protected more strictly with the write lock. In this way concurrent access is possible to the bean as long as it is
|
||||
* not being destroyed, in which case only one thread has access. If the bean has no destruction callback the lock and
|
||||
* associated proxies are never created.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class StandardBeanLifecycleDecorator implements BeanLifecycleDecorator<ReadWriteLock> {
|
||||
|
||||
private final boolean proxyTargetClass;
|
||||
|
||||
public StandardBeanLifecycleDecorator(boolean proxyTargetClass) {
|
||||
this.proxyTargetClass = proxyTargetClass;
|
||||
}
|
||||
|
||||
public Object decorateBean(Object bean, Context<ReadWriteLock> context) {
|
||||
if (context != null) {
|
||||
bean = getDisposalLockProxy(bean, context.getAuxiliary().readLock());
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
public Context<ReadWriteLock> decorateDestructionCallback(final Runnable callback) {
|
||||
if (callback == null) {
|
||||
return null;
|
||||
}
|
||||
final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
|
||||
return new Context<ReadWriteLock>(new Runnable() {
|
||||
public void run() {
|
||||
Lock lock = readWriteLock.writeLock();
|
||||
lock.lock();
|
||||
try {
|
||||
callback.run();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
}, readWriteLock);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a lock (preferably a read lock allowing multiple concurrent access) to the bean. Callers should replace the
|
||||
* bean input with the output.
|
||||
*
|
||||
* @param bean the bean to lock
|
||||
* @param lock the lock to apply
|
||||
* @return a proxy that locks while its methods are executed
|
||||
*/
|
||||
private Object getDisposalLockProxy(Object bean, final Lock lock) {
|
||||
ProxyFactory factory = new ProxyFactory(bean);
|
||||
factory.setProxyTargetClass(proxyTargetClass);
|
||||
factory.addAdvice(new MethodInterceptor() {
|
||||
public Object invoke(MethodInvocation invocation) throws Throwable {
|
||||
lock.lock();
|
||||
try {
|
||||
return invocation.proceed();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
});
|
||||
return factory.getProxy();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2014 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.context.config.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.context.annotation.ScopedProxyMode;
|
||||
|
||||
/**
|
||||
* Convenience annotation to put a <code>@Bean</code> definition in
|
||||
* {@link org.springframework.cloud.context.scope.refresh.RefreshScope refresh scope}.
|
||||
* Beans annotated this way can be refreshed at runtime and any components that are using
|
||||
* them will get a new instance on the next method call, fully initialized and injected
|
||||
* with all dependencies.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Scope("refresh")
|
||||
@Documented
|
||||
public @interface RefreshScope {
|
||||
/**
|
||||
* @see Scope#proxyMode()
|
||||
*/
|
||||
ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;
|
||||
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2014 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.context.environment;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
/**
|
||||
* Event published to signal a change in the {@link Environment}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class EnvironmentChangeEvent extends ApplicationEvent {
|
||||
|
||||
private Set<String> keys;
|
||||
|
||||
public EnvironmentChangeEvent(Set<String> keys) {
|
||||
super(keys);
|
||||
this.keys = keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the keys
|
||||
*/
|
||||
public Set<String> getKeys() {
|
||||
return keys;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2014 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.context.environment;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.ApplicationEventPublisherAware;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
import org.springframework.core.env.MutablePropertySources;
|
||||
import org.springframework.jmx.export.annotation.ManagedOperation;
|
||||
import org.springframework.jmx.export.annotation.ManagedResource;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Entry point for making local (but volatile) changes to the {@link Environment} of a
|
||||
* running application. Allows properties to be added and values changed, simply by adding
|
||||
* them to a high priority property source in the existing Environment.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@Component
|
||||
@ManagedResource
|
||||
public class EnvironmentManager implements ApplicationEventPublisherAware {
|
||||
|
||||
private static final String MANAGER_PROPERTY_SOURCE = "manager";
|
||||
private Map<String, Object> map = new LinkedHashMap<String, Object>();
|
||||
|
||||
private ConfigurableEnvironment environment;
|
||||
private ApplicationEventPublisher publisher;
|
||||
|
||||
public EnvironmentManager(ConfigurableEnvironment environment) {
|
||||
this.environment = environment;
|
||||
MutablePropertySources sources = environment.getPropertySources();
|
||||
if (sources.contains(MANAGER_PROPERTY_SOURCE)) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> map = (Map<String, Object>) sources.get(
|
||||
MANAGER_PROPERTY_SOURCE).getSource();
|
||||
this.map = map;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
|
||||
this.publisher = publisher;
|
||||
}
|
||||
|
||||
@ManagedOperation
|
||||
public Map<String, Object> reset() {
|
||||
Map<String, Object> result = new LinkedHashMap<String, Object>(map);
|
||||
if (!map.isEmpty()) {
|
||||
Set<String> keys = map.keySet();
|
||||
map.clear();
|
||||
publish(new EnvironmentChangeEvent(keys));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@ManagedOperation
|
||||
public void setProperty(String name, String value) {
|
||||
|
||||
if (!environment.getPropertySources().contains(MANAGER_PROPERTY_SOURCE)) {
|
||||
synchronized (map) {
|
||||
if (!environment.getPropertySources().contains(MANAGER_PROPERTY_SOURCE)) {
|
||||
MapPropertySource source = new MapPropertySource(
|
||||
MANAGER_PROPERTY_SOURCE, map);
|
||||
environment.getPropertySources().addFirst(source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!value.equals(environment.getProperty(name))) {
|
||||
map.put(name, value);
|
||||
publish(new EnvironmentChangeEvent(Collections.singleton(name)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ManagedOperation
|
||||
public Object getProperty(String name) {
|
||||
return environment.getProperty(name);
|
||||
}
|
||||
|
||||
private void publish(EnvironmentChangeEvent environmentChangeEvent) {
|
||||
if (publisher != null) {
|
||||
publisher.publishEvent(environmentChangeEvent);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2014 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.context.environment;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.EnvironmentEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
/**
|
||||
* MVC endpoint for the {@link EnvironmentManager} providing a POST to /env as a simple
|
||||
* way to change the Environment.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class EnvironmentManagerMvcEndpoint implements MvcEndpoint {
|
||||
|
||||
private EnvironmentManager environment;
|
||||
private EnvironmentEndpoint delegate;
|
||||
|
||||
public EnvironmentManagerMvcEndpoint(EnvironmentEndpoint delegate,
|
||||
EnvironmentManager enviroment) {
|
||||
this.delegate = delegate;
|
||||
environment = enviroment;
|
||||
}
|
||||
|
||||
@RequestMapping(value = "", method = RequestMethod.POST)
|
||||
@ResponseBody
|
||||
public Object value(@RequestParam Map<String, String> params) {
|
||||
for (String name : params.keySet()) {
|
||||
environment.setProperty(name, params.get(name));
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
@RequestMapping(value = "reset", method = RequestMethod.POST)
|
||||
@ResponseBody
|
||||
public Map<String, Object> reset() {
|
||||
return environment.reset();
|
||||
}
|
||||
|
||||
public void setEnvironmentManager(EnvironmentManager environment) {
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPath() {
|
||||
return "/" + this.delegate.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSensitive() {
|
||||
return this.delegate.isSensitive();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("rawtypes")
|
||||
public Class<? extends Endpoint> getEndpointType() {
|
||||
return this.delegate.getClass();
|
||||
}
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2014 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.context.properties;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.boot.context.properties.ConfigurationBeanFactoryMetaData;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor;
|
||||
import org.springframework.cloud.context.config.annotation.RefreshScope;
|
||||
import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.jmx.export.annotation.ManagedAttribute;
|
||||
import org.springframework.jmx.export.annotation.ManagedOperation;
|
||||
import org.springframework.jmx.export.annotation.ManagedResource;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Listens for {@link EnvironmentChangeEvent} and rebinds beans that were bound to the
|
||||
* {@link Environment} using {@link ConfigurationProperties
|
||||
* <code>@ConfigurationProperties</code>}. When these beans are re-bound and
|
||||
* re-initialized the changes are available immediately to any component that is using the
|
||||
* <code>@ConfigurationProperties</code> bean.
|
||||
*
|
||||
* @see RefreshScope for a deeper and optionally more focused refresh of bean components
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@Component
|
||||
@ManagedResource
|
||||
public class ConfigurationPropertiesRebinder implements BeanPostProcessor,
|
||||
ApplicationListener<EnvironmentChangeEvent>, ApplicationContextAware {
|
||||
|
||||
private ConfigurationBeanFactoryMetaData metaData;
|
||||
|
||||
private ConfigurationPropertiesBindingPostProcessor binder;
|
||||
|
||||
public ConfigurationPropertiesRebinder(
|
||||
ConfigurationPropertiesBindingPostProcessor binder) {
|
||||
this.binder = binder;
|
||||
}
|
||||
|
||||
private Map<String, Object> beans = new HashMap<String, Object>();
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext)
|
||||
throws BeansException {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param beans the bean meta data to set
|
||||
*/
|
||||
public void setBeanMetaDataStore(ConfigurationBeanFactoryMetaData beans) {
|
||||
this.metaData = beans;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName)
|
||||
throws BeansException {
|
||||
ConfigurationProperties annotation = AnnotationUtils.findAnnotation(
|
||||
bean.getClass(), ConfigurationProperties.class);
|
||||
if (annotation != null) {
|
||||
beans.put(beanName, bean);
|
||||
}
|
||||
else if (metaData != null) {
|
||||
annotation = this.metaData.findFactoryAnnotation(beanName,
|
||||
ConfigurationProperties.class);
|
||||
if (annotation != null) {
|
||||
beans.put(beanName, bean);
|
||||
}
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName)
|
||||
throws BeansException {
|
||||
return bean;
|
||||
}
|
||||
|
||||
@ManagedOperation
|
||||
public void rebind() {
|
||||
for (String name : beans.keySet()) {
|
||||
rebind(name);
|
||||
}
|
||||
}
|
||||
|
||||
@ManagedOperation
|
||||
public void rebind(String name) {
|
||||
binder.postProcessBeforeInitialization(beans.get(name), name);
|
||||
if (applicationContext != null) {
|
||||
applicationContext.getAutowireCapableBeanFactory().initializeBean(
|
||||
beans.get(name), name);
|
||||
}
|
||||
}
|
||||
|
||||
@ManagedAttribute
|
||||
public Set<String> getBeanNames() {
|
||||
return new HashSet<String>(beans.keySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(EnvironmentChangeEvent event) {
|
||||
rebind();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,203 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2014 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.context.restart;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.actuate.endpoint.AbstractEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.Endpoint;
|
||||
import org.springframework.boot.context.event.ApplicationPreparedEvent;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.integration.monitor.IntegrationMBeanExporter;
|
||||
import org.springframework.jmx.export.annotation.ManagedAttribute;
|
||||
import org.springframework.jmx.export.annotation.ManagedOperation;
|
||||
import org.springframework.jmx.export.annotation.ManagedResource;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* An endpoint that restarts the application context. Install as a bean and also register
|
||||
* a {@link RestartListener} with the {@link SpringApplication} that starts the context.
|
||||
* Those two components communicate via an {@link ApplicationEvent} and set up the state
|
||||
* needed to restart the context.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@ConfigurationProperties("endpoints.restart")
|
||||
@ManagedResource
|
||||
public class RestartEndpoint extends AbstractEndpoint<Boolean> implements
|
||||
ApplicationListener<ApplicationPreparedEvent> {
|
||||
|
||||
private static Log logger = LogFactory.getLog(RestartEndpoint.class);
|
||||
|
||||
public RestartEndpoint() {
|
||||
super("restart", true, false);
|
||||
}
|
||||
|
||||
private ConfigurableApplicationContext context;
|
||||
|
||||
private SpringApplication application;
|
||||
|
||||
private String[] args;
|
||||
|
||||
private ApplicationPreparedEvent event;
|
||||
|
||||
private IntegrationShutdown integrationShutdown;
|
||||
|
||||
private long timeout;
|
||||
|
||||
@ManagedAttribute
|
||||
public long getTimeout() {
|
||||
return timeout;
|
||||
}
|
||||
|
||||
public void setTimeout(long timeout) {
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
public void setIntegrationMBeanExporter(Object exporter) {
|
||||
if (exporter != null) {
|
||||
this.integrationShutdown = new IntegrationShutdown(exporter);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(ApplicationPreparedEvent input) {
|
||||
event = (ApplicationPreparedEvent) input;
|
||||
if (context == null) {
|
||||
context = event.getApplicationContext();
|
||||
args = event.getArgs();
|
||||
application = event.getSpringApplication();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean invoke() {
|
||||
try {
|
||||
restart();
|
||||
logger.info("Restarted");
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.info("Could not restart", e);
|
||||
} else {
|
||||
logger.info("Could not restart: " + e.getMessage());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public Endpoint<Boolean> getPauseEndpoint() {
|
||||
return new PauseEndpoint();
|
||||
}
|
||||
|
||||
public Endpoint<Boolean> getResumeEndpoint() {
|
||||
return new ResumeEndpoint();
|
||||
}
|
||||
|
||||
private class PauseEndpoint extends AbstractEndpoint<Boolean> {
|
||||
|
||||
public PauseEndpoint() {
|
||||
super("pause", true, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean invoke() {
|
||||
if (isRunning()) {
|
||||
pause();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private class ResumeEndpoint extends AbstractEndpoint<Boolean> {
|
||||
|
||||
public ResumeEndpoint() {
|
||||
super("resume", true, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean invoke() {
|
||||
if (!isRunning()) {
|
||||
resume();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ManagedOperation
|
||||
public synchronized ConfigurableApplicationContext restart() {
|
||||
if (context != null) {
|
||||
if (integrationShutdown != null) {
|
||||
integrationShutdown.stop(timeout);
|
||||
}
|
||||
application.setEnvironment(context.getEnvironment());
|
||||
context.close();
|
||||
// If running in a webapp then the context classloader is probably going to
|
||||
// die so we need to revert to a safe place before starting again
|
||||
overrideClassLoaderForRestart();
|
||||
context = application.run(args);
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
@ManagedAttribute
|
||||
public boolean isRunning() {
|
||||
if (context != null) {
|
||||
return context.isRunning();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@ManagedOperation
|
||||
public synchronized void pause() {
|
||||
if (context != null) {
|
||||
context.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@ManagedOperation
|
||||
public synchronized void resume() {
|
||||
if (context != null) {
|
||||
context.start();
|
||||
}
|
||||
}
|
||||
|
||||
private void overrideClassLoaderForRestart() {
|
||||
ClassUtils.overrideThreadContextClassLoader(application.getClass().getClassLoader());
|
||||
}
|
||||
|
||||
private class IntegrationShutdown {
|
||||
|
||||
private IntegrationMBeanExporter exporter;
|
||||
|
||||
public IntegrationShutdown(Object exporter) {
|
||||
this.exporter = (IntegrationMBeanExporter) exporter;
|
||||
}
|
||||
|
||||
public void stop(long timeout) {
|
||||
exporter.stopActiveComponents(timeout);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2014 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.context.restart;
|
||||
|
||||
import org.springframework.boot.context.event.ApplicationPreparedEvent;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.event.ContextClosedEvent;
|
||||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
import org.springframework.context.event.SmartApplicationListener;
|
||||
|
||||
/**
|
||||
* A listener that stores enough information about an application as it starts, to be able
|
||||
* to restart it later if needed.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class RestartListener implements SmartApplicationListener {
|
||||
|
||||
private ConfigurableApplicationContext context;
|
||||
|
||||
private ApplicationPreparedEvent event;
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
|
||||
return ApplicationPreparedEvent.class.isAssignableFrom(eventType)
|
||||
|| ContextRefreshedEvent.class.isAssignableFrom(eventType)
|
||||
|| ContextClosedEvent.class.isAssignableFrom(eventType);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsSourceType(Class<?> sourceType) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(ApplicationEvent input) {
|
||||
if (input instanceof ApplicationPreparedEvent) {
|
||||
event = (ApplicationPreparedEvent) input;
|
||||
if (context == null) {
|
||||
context = event.getApplicationContext();
|
||||
}
|
||||
}
|
||||
else if (input instanceof ContextRefreshedEvent) {
|
||||
if (context != null && input.getSource().equals(context) && event != null) {
|
||||
context.publishEvent(event);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (context != null && input.getSource().equals(context)) {
|
||||
context = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2014 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.context.restart;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter;
|
||||
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.cloud.endpoint.GenericPostableMvcEndpoint;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
/**
|
||||
* MVC endpoint to allow an application to be restarted on a POST (to /restart by
|
||||
* default).
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class RestartMvcEndpoint extends EndpointMvcAdapter {
|
||||
|
||||
public RestartMvcEndpoint(RestartEndpoint delegate) {
|
||||
super(delegate);
|
||||
}
|
||||
|
||||
@RequestMapping(method = RequestMethod.POST)
|
||||
@ResponseBody
|
||||
@Override
|
||||
public Object invoke() {
|
||||
if (!getDelegate().isEnabled()) {
|
||||
return new ResponseEntity<Map<String, String>>(Collections.singletonMap(
|
||||
"message", "This endpoint is disabled"), HttpStatus.NOT_FOUND);
|
||||
}
|
||||
Thread thread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
RestartMvcEndpoint.super.invoke();
|
||||
}
|
||||
});
|
||||
thread.setDaemon(false);
|
||||
thread.start();
|
||||
return Collections.singletonMap("message", "Restarting");
|
||||
}
|
||||
|
||||
public MvcEndpoint getPauseEndpoint() {
|
||||
return new GenericPostableMvcEndpoint(
|
||||
((RestartEndpoint) getDelegate()).getPauseEndpoint());
|
||||
}
|
||||
|
||||
public MvcEndpoint getResumeEndpoint() {
|
||||
return new GenericPostableMvcEndpoint(
|
||||
((RestartEndpoint) getDelegate()).getResumeEndpoint());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,354 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 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. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.context.scope;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.ObjectFactory;
|
||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.config.Scope;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.cloud.context.config.BeanLifecycleDecorator;
|
||||
import org.springframework.cloud.context.config.BeanLifecycleDecorator.Context;
|
||||
import org.springframework.cloud.context.config.StandardBeanLifecycleDecorator;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.ParseException;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A generic Scope implementation.
|
||||
* </p>
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
* @since 3.1
|
||||
*
|
||||
*/
|
||||
public class GenericScope implements Scope, BeanFactoryPostProcessor, DisposableBean {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(GenericScope.class);
|
||||
|
||||
public static final String SCOPED_TARGET_PREFIX = "scopedTarget.";
|
||||
|
||||
private BeanLifecycleWrapperCache cache = new BeanLifecycleWrapperCache(
|
||||
new StandardScopeCache());
|
||||
|
||||
private String name = "generic";
|
||||
|
||||
private boolean proxyTargetClass = true;
|
||||
|
||||
private ConfigurableListableBeanFactory beanFactory;
|
||||
|
||||
private StandardEvaluationContext evaluationContext;
|
||||
|
||||
private String id;
|
||||
|
||||
private BeanLifecycleDecorator<?> lifecycle;
|
||||
|
||||
/**
|
||||
* Manual override for the serialization id that will be used to identify the bean
|
||||
* factory. The default is a unique key based on the bean names in the bean factory.
|
||||
*
|
||||
* @param id the id to set
|
||||
*/
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of this scope. Default "refresh".
|
||||
*
|
||||
* @param name the name value to set
|
||||
*/
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flag to indicate that proxies should be created for the concrete type, not just the
|
||||
* interfaces, of the scoped beans.
|
||||
*
|
||||
* @param proxyTargetClass the flag value to set
|
||||
*/
|
||||
public void setProxyTargetClass(boolean proxyTargetClass) {
|
||||
this.proxyTargetClass = proxyTargetClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* The cache implementation to use for bean instances in this scope.
|
||||
*
|
||||
* @param cache the cache to use
|
||||
*/
|
||||
public void setScopeCache(ScopeCache cache) {
|
||||
this.cache = new BeanLifecycleWrapperCache(cache);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to manage the creation and destruction of beans.
|
||||
*
|
||||
* @param lifecycle the bean lifecycle to set
|
||||
*/
|
||||
public void setBeanLifecycleManager(BeanLifecycleDecorator<?> lifecycle) {
|
||||
this.lifecycle = lifecycle;
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
List<Throwable> errors = new ArrayList<Throwable>();
|
||||
Collection<BeanLifecycleWrapper> wrappers = cache.clear();
|
||||
for (BeanLifecycleWrapper wrapper : wrappers) {
|
||||
try {
|
||||
wrapper.destroy();
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
errors.add(e);
|
||||
}
|
||||
}
|
||||
if (!errors.isEmpty()) {
|
||||
throw wrapIfNecessary(errors.get(0));
|
||||
}
|
||||
}
|
||||
|
||||
protected void destroy(String name) {
|
||||
BeanLifecycleWrapper wrapper = cache.remove(name);
|
||||
if (wrapper != null) {
|
||||
wrapper.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
public Object get(String name, ObjectFactory<?> objectFactory) {
|
||||
if (lifecycle == null) {
|
||||
lifecycle = new StandardBeanLifecycleDecorator(proxyTargetClass);
|
||||
}
|
||||
BeanLifecycleWrapper value = cache.put(name, new BeanLifecycleWrapper(name,
|
||||
objectFactory, lifecycle));
|
||||
return value.getBean();
|
||||
}
|
||||
|
||||
public String getConversationId() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void registerDestructionCallback(String name, Runnable callback) {
|
||||
BeanLifecycleWrapper value = cache.get(name);
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
value.setDestroyCallback(callback);
|
||||
}
|
||||
|
||||
public Object remove(String name) {
|
||||
BeanLifecycleWrapper value = cache.remove(name);
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
// Someone might have added another object with the same key, but we
|
||||
// keep the method contract by removing the
|
||||
// value we found anyway
|
||||
return value.getBean();
|
||||
}
|
||||
|
||||
public Object resolveContextualObject(String key) {
|
||||
Expression expression = parseExpression(key);
|
||||
return expression.getValue(evaluationContext, beanFactory);
|
||||
}
|
||||
|
||||
private Expression parseExpression(String input) {
|
||||
if (StringUtils.hasText(input)) {
|
||||
ExpressionParser parser = new SpelExpressionParser();
|
||||
try {
|
||||
return parser.parseExpression(input);
|
||||
}
|
||||
catch (ParseException e) {
|
||||
throw new IllegalArgumentException("Cannot parse expression: " + input, e);
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
|
||||
throws BeansException {
|
||||
beanFactory.registerScope(name, this);
|
||||
setSerializationId(beanFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the bean factory is a DefaultListableBeanFactory then it can serialize scoped
|
||||
* beans and deserialize them in another context (even in another JVM), as long as the
|
||||
* ids of the bean factories match. This method sets up the serialization id to be
|
||||
* either the id provided to the scope instance, or if that is null, a hash of all the
|
||||
* bean names.
|
||||
*
|
||||
* @param beanFactory the bean factory to configure
|
||||
*/
|
||||
private void setSerializationId(ConfigurableListableBeanFactory beanFactory) {
|
||||
|
||||
if (beanFactory instanceof DefaultListableBeanFactory) {
|
||||
|
||||
String id = this.id;
|
||||
if (id == null) {
|
||||
String names = Arrays.asList(beanFactory.getBeanDefinitionNames())
|
||||
.toString();
|
||||
logger.debug("Generating bean factory id from names: " + names);
|
||||
id = UUID.nameUUIDFromBytes(names.getBytes()).toString();
|
||||
}
|
||||
|
||||
logger.info("BeanFactory id=" + id);
|
||||
((DefaultListableBeanFactory) beanFactory).setSerializationId(id);
|
||||
|
||||
}
|
||||
else {
|
||||
logger.warn("BeanFactory was not a DefaultListableBeanFactory, so RefreshScope beans "
|
||||
+ "cannot be serialized reliably and passed to a remote JVM.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static RuntimeException wrapIfNecessary(Throwable throwable) {
|
||||
if (throwable instanceof RuntimeException) {
|
||||
return (RuntimeException) throwable;
|
||||
}
|
||||
if (throwable instanceof Error) {
|
||||
throw (Error) throwable;
|
||||
}
|
||||
return new IllegalStateException(throwable);
|
||||
}
|
||||
|
||||
private static class BeanLifecycleWrapperCache {
|
||||
|
||||
private final ScopeCache cache;
|
||||
|
||||
public BeanLifecycleWrapperCache(ScopeCache cache) {
|
||||
this.cache = cache;
|
||||
}
|
||||
|
||||
public BeanLifecycleWrapper remove(String name) {
|
||||
return (BeanLifecycleWrapper) cache.remove(name);
|
||||
}
|
||||
|
||||
public Collection<BeanLifecycleWrapper> clear() {
|
||||
Collection<Object> values = cache.clear();
|
||||
Collection<BeanLifecycleWrapper> wrappers = new LinkedHashSet<BeanLifecycleWrapper>();
|
||||
for (Object object : values) {
|
||||
wrappers.add((BeanLifecycleWrapper) object);
|
||||
}
|
||||
return wrappers;
|
||||
}
|
||||
|
||||
public BeanLifecycleWrapper get(String name) {
|
||||
return (BeanLifecycleWrapper) cache.get(name);
|
||||
}
|
||||
|
||||
public BeanLifecycleWrapper put(String name, BeanLifecycleWrapper value) {
|
||||
return (BeanLifecycleWrapper) cache.put(name, (Object) value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for a bean instance and any destruction callback (DisposableBean etc.) that
|
||||
* is registered for it. Also decorates the bean to optionally guard it from
|
||||
* concurrent access (for instance).
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
private static class BeanLifecycleWrapper {
|
||||
|
||||
private Object bean;
|
||||
|
||||
private Context<?> context;
|
||||
|
||||
private final String name;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
private final BeanLifecycleDecorator lifecycle;
|
||||
|
||||
private final ObjectFactory<?> objectFactory;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public BeanLifecycleWrapper(String name, ObjectFactory<?> objectFactory,
|
||||
BeanLifecycleDecorator lifecycle) {
|
||||
this.name = name;
|
||||
this.objectFactory = objectFactory;
|
||||
this.lifecycle = lifecycle;
|
||||
}
|
||||
|
||||
public void setDestroyCallback(Runnable callback) {
|
||||
this.context = lifecycle.decorateDestructionCallback(callback);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Object getBean() {
|
||||
if (bean == null) {
|
||||
bean = lifecycle.decorateBean(objectFactory.getObject(), context);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
if (context == null) {
|
||||
return;
|
||||
}
|
||||
Runnable callback = context.getCallback();
|
||||
if (callback != null) {
|
||||
callback.run();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((name == null) ? 0 : name.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
BeanLifecycleWrapper other = (BeanLifecycleWrapper) obj;
|
||||
if (name == null) {
|
||||
if (other.name != null)
|
||||
return false;
|
||||
}
|
||||
else if (!name.equals(other.name))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.context.scope;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* A special purpose cache interface specifically for the {@link GenericScope} to use to manage cached bean instances.
|
||||
* Implementations generally fall into two categories: those that store values "globally" (i.e. one instance per key),
|
||||
* and those that store potentially multiple instances per key based on context (e.g. via a thread local). All
|
||||
* implementations should be thread safe.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public interface ScopeCache {
|
||||
|
||||
/**
|
||||
* Remove the object with this name from the cache.
|
||||
*
|
||||
* @param name the object name
|
||||
* @return the object removed or null if there was none
|
||||
*/
|
||||
Object remove(String name);
|
||||
|
||||
/**
|
||||
* Clear the cache and return all objects in an unmodifiable collection.
|
||||
*
|
||||
* @return all objects stored in the cache
|
||||
*/
|
||||
Collection<Object> clear();
|
||||
|
||||
/**
|
||||
* Get the named object from the cache.
|
||||
*
|
||||
* @param name the name of the object
|
||||
* @return the object with that name or null if there is none
|
||||
*/
|
||||
Object get(String name);
|
||||
|
||||
/**
|
||||
* Put a value in the cache if the key is not already used. If one is already present with the name provided, it is
|
||||
* not replaced, but is returned to the caller.
|
||||
*
|
||||
* @param name the key
|
||||
* @param value the new candidate value
|
||||
* @return the value that is in the cache at the end of the operation
|
||||
*/
|
||||
Object put(String name, Object value);
|
||||
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.context.scope;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
* A simple cache implementation backed by a concurrent map.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class StandardScopeCache implements ScopeCache {
|
||||
|
||||
private final ConcurrentMap<String, Object> cache = new ConcurrentHashMap<String, Object>();
|
||||
|
||||
public Object remove(String name) {
|
||||
return cache.remove(name);
|
||||
}
|
||||
|
||||
public Collection<Object> clear() {
|
||||
Collection<Object> values = new ArrayList<Object>(cache.values());
|
||||
cache.clear();
|
||||
return values;
|
||||
}
|
||||
|
||||
public Object get(String name) {
|
||||
return cache.get(name);
|
||||
}
|
||||
|
||||
public Object put(String name, Object value) {
|
||||
Object result = cache.putIfAbsent(name, value);
|
||||
if (result!=null) {
|
||||
return result;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 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. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.context.scope.refresh;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.cloud.context.scope.GenericScope;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.jmx.export.annotation.ManagedOperation;
|
||||
import org.springframework.jmx.export.annotation.ManagedResource;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A Scope implementation that allows for beans to be refreshed dynamically at runtime (see {@link #refresh(String)} and
|
||||
* {@link #refreshAll()}). If a bean is refreshed then the next time the bean is accessed (i.e. a method is executed) a
|
||||
* new instance is created. All lifecycle methods are applied to the bean instances, so any destruction callbacks that
|
||||
* were registered in the bean factory are called when it is refreshed, and then the initialization callbacks are
|
||||
* invoked as normal when the new instance is created. A new bean instance is created from the original bean definition,
|
||||
* so any externalized content (property placeholders or expressions in string literals) is re-evaluated when it is
|
||||
* created.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Note that all beans in this scope are <em>only</em> initialized when first accessed, so the scope forces lazy
|
||||
* initialization semantics. The implementation involves creating a proxy for every bean in the scope, so there is a
|
||||
* flag {@link #setProxyTargetClass(boolean) proxyTargetClass} which controls the proxy creation, defaulting to JDK
|
||||
* dynamic proxies and therefore only exposing the interfaces implemented by a bean. If callers need access to other
|
||||
* methods then the flag needs to be set (and CGLib present on the classpath). Because this scope automatically proxies
|
||||
* all its beans, there is no need to add <code><aop:auto-proxy/></code> to any bean definitions.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* The scoped proxy approach adopted here has a side benefit that bean instances are automatically {@link Serializable},
|
||||
* and can be sent across the wire as long as the receiver has an identical application context on the other side. To
|
||||
* ensure that the two contexts agree that they are identical they have to have the same serialization id. One will be
|
||||
* generated automatically by default from the bean names, so two contexts with the same bean names are by default able
|
||||
* to exchange beans by name. If you need to override the default id then provide an explicit {@link #setId(String) id}
|
||||
* when the Scope is declared.
|
||||
* </p>
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
* @since 3.1
|
||||
*
|
||||
*/
|
||||
@ManagedResource
|
||||
public class RefreshScope extends GenericScope implements ApplicationContextAware {
|
||||
|
||||
private ApplicationContext context;
|
||||
|
||||
/**
|
||||
* Create a scope instance and give it the default name: "refresh".
|
||||
*/
|
||||
public RefreshScope() {
|
||||
super();
|
||||
super.setName("refresh");
|
||||
}
|
||||
|
||||
@ManagedOperation(description = "Dispose of the current instance of bean name provided and force a refresh on next method execution.")
|
||||
public void refresh(String name) {
|
||||
if (!name.startsWith(SCOPED_TARGET_PREFIX)) {
|
||||
// User wants to refresh the bean with this name but that isn't the one in the cache...
|
||||
name = SCOPED_TARGET_PREFIX + name;
|
||||
}
|
||||
// Ensure lifecycle is finished if bean was disposable
|
||||
super.destroy(name);
|
||||
context.publishEvent(new RefreshScopeRefreshedEvent(name));
|
||||
}
|
||||
|
||||
@ManagedOperation(description = "Dispose of the current instance of all beans in this scope and force a refresh on next method execution.")
|
||||
public void refreshAll() {
|
||||
super.destroy();
|
||||
context.publishEvent(new RefreshScopeRefreshedEvent());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext context) throws BeansException {
|
||||
this.context = context;
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package org.springframework.cloud.context.scope.refresh;
|
||||
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class RefreshScopeRefreshedEvent extends ApplicationEvent {
|
||||
public static final String DEFAULT_NAME = "__refreshAll__";
|
||||
private String name;
|
||||
|
||||
public RefreshScopeRefreshedEvent() {
|
||||
this(DEFAULT_NAME);
|
||||
}
|
||||
|
||||
public RefreshScopeRefreshedEvent(String name) {
|
||||
super(name);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.context.scope.thread;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import org.springframework.cloud.context.scope.ScopeCache;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class ThreadLocalScopeCache implements ScopeCache {
|
||||
|
||||
private ThreadLocal<ConcurrentMap<String, Object>> data = new ThreadLocal<ConcurrentMap<String, Object>>() {
|
||||
protected ConcurrentMap<String, Object> initialValue() {
|
||||
return new ConcurrentHashMap<String, Object>();
|
||||
}
|
||||
};
|
||||
|
||||
public Object remove(String name) {
|
||||
return data.get().remove(name);
|
||||
}
|
||||
|
||||
public Collection<Object> clear() {
|
||||
ConcurrentMap<String, Object> map = data.get();
|
||||
Collection<Object> values = new ArrayList<Object>(map.values());
|
||||
map.clear();
|
||||
return values;
|
||||
}
|
||||
|
||||
public Object get(String name) {
|
||||
return data.get().get(name);
|
||||
}
|
||||
|
||||
public Object put(String name, Object value) {
|
||||
Object result = data.get().putIfAbsent(name, value);
|
||||
if (result!=null) {
|
||||
return result;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 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. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.context.scope.thread;
|
||||
|
||||
import org.springframework.cloud.context.scope.GenericScope;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
* @since 3.1
|
||||
*
|
||||
*/
|
||||
public class ThreadScope extends GenericScope {
|
||||
|
||||
/**
|
||||
* Create a scope instance and give it the default name: "thread".
|
||||
*/
|
||||
public ThreadScope() {
|
||||
super();
|
||||
super.setName("thread");
|
||||
super.setScopeCache(new ThreadLocalScopeCache());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2014 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.endpoint;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
/**
|
||||
* A convenient base class for MVC endpoints that accept a POST (instead of the default
|
||||
* GET).
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class GenericPostableMvcEndpoint extends EndpointMvcAdapter {
|
||||
|
||||
public GenericPostableMvcEndpoint(Endpoint<?> delegate) {
|
||||
super(delegate);
|
||||
}
|
||||
|
||||
@RequestMapping(method = RequestMethod.POST)
|
||||
@ResponseBody
|
||||
@Override
|
||||
public Object invoke() {
|
||||
if (!getDelegate().isEnabled()) {
|
||||
return new ResponseEntity<Map<String, String>>(Collections.singletonMap(
|
||||
"message", "This endpoint is disabled"), HttpStatus.NOT_FOUND);
|
||||
}
|
||||
return super.invoke();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2014 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.logging;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.boot.bind.RelaxedPropertyResolver;
|
||||
import org.springframework.boot.logging.LogLevel;
|
||||
import org.springframework.boot.logging.LoggingSystem;
|
||||
import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.EnvironmentAware;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
/**
|
||||
* Listener that looks for {@link EnvironmentChangeEvent} and rebinds logger levels if any
|
||||
* changed.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class LoggingRebinder implements ApplicationListener<EnvironmentChangeEvent>,
|
||||
EnvironmentAware {
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private Environment environment;
|
||||
|
||||
@Override
|
||||
public void setEnvironment(Environment environment) {
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(EnvironmentChangeEvent event) {
|
||||
if (environment == null) {
|
||||
return;
|
||||
}
|
||||
LoggingSystem system = LoggingSystem.get(LoggingSystem.class.getClassLoader());
|
||||
setLogLevels(system, environment);
|
||||
}
|
||||
|
||||
protected void setLogLevels(LoggingSystem system, Environment environment) {
|
||||
Map<String, Object> levels = new RelaxedPropertyResolver(environment)
|
||||
.getSubProperties("logging.level.");
|
||||
for (Entry<String, Object> entry : levels.entrySet()) {
|
||||
setLogLevel(system, environment, entry.getKey(), entry.getValue().toString());
|
||||
}
|
||||
}
|
||||
|
||||
private void setLogLevel(LoggingSystem system, Environment environment, String name,
|
||||
String level) {
|
||||
try {
|
||||
if (name.equalsIgnoreCase("root")) {
|
||||
name = null;
|
||||
}
|
||||
level = environment.resolvePlaceholders(level);
|
||||
system.setLogLevel(name, LogLevel.valueOf(level));
|
||||
}
|
||||
catch (RuntimeException ex) {
|
||||
this.logger.error("Cannot set level: " + level + " for '" + name + "'");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,16 +1,7 @@
|
||||
# Auto Configure
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.springframework.cloud.autoconfigure.RefreshAutoConfiguration,\
|
||||
org.springframework.cloud.autoconfigure.LifecycleMvcEndpointAutoConfiguration,\
|
||||
org.springframework.cloud.autoconfigure.ConfigClientAutoConfiguration
|
||||
|
||||
# Application Listeners
|
||||
org.springframework.context.ApplicationListener=\
|
||||
org.springframework.cloud.bootstrap.BootstrapApplicationListener,\
|
||||
org.springframework.cloud.context.restart.RestartListener
|
||||
org.springframework.cloud.config.client.ConfigClientAutoConfiguration
|
||||
|
||||
# Bootstrap components
|
||||
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
|
||||
org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration,\
|
||||
org.springframework.cloud.bootstrap.encrypt.EncryptionBootstrapConfiguration,\
|
||||
org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration
|
||||
org.springframework.cloud.config.client.ConfigServiceBootstrapConfiguration
|
||||
@@ -5,9 +5,8 @@ import static org.junit.Assert.assertEquals;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration;
|
||||
import org.springframework.cloud.config.client.ConfigClientAutoConfiguration;
|
||||
import org.springframework.cloud.config.client.ConfigClientProperties;
|
||||
import org.springframework.cloud.config.client.ConfigServerHealthIndicator;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
|
||||
@@ -31,16 +30,4 @@ public class ConfigClientAutoConfigurationTests {
|
||||
context.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withHealthIndicator() {
|
||||
ConfigurableApplicationContext context = new SpringApplicationBuilder(
|
||||
PropertySourceBootstrapConfiguration.class)
|
||||
.child(ConfigClientAutoConfiguration.class).web(false).run();
|
||||
assertEquals(1, BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context,
|
||||
ConfigClientProperties.class).length);
|
||||
assertEquals(1, BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context,
|
||||
ConfigServerHealthIndicator.class).length);
|
||||
context.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package org.springframework.cloud.autoconfigure;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration;
|
||||
import org.springframework.cloud.config.client.ConfigClientAutoConfiguration;
|
||||
import org.springframework.cloud.config.client.ConfigClientProperties;
|
||||
import org.springframework.cloud.config.client.ConfigServerHealthIndicator;
|
||||
import org.springframework.cloud.config.client.ConfigServiceBootstrapConfiguration;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
public class ConfigServerBootstrapConfigurationTests {
|
||||
|
||||
@Test
|
||||
public void withHealthIndicator() {
|
||||
ConfigurableApplicationContext context = new SpringApplicationBuilder(
|
||||
PropertySourceBootstrapConfiguration.class, ConfigServiceBootstrapConfiguration.class)
|
||||
.child(ConfigClientAutoConfiguration.class).web(false).run();
|
||||
assertEquals(1, BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context,
|
||||
ConfigClientProperties.class).length);
|
||||
assertEquals(1, BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context,
|
||||
ConfigServerHealthIndicator.class).length);
|
||||
context.close();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
package org.springframework.cloud.bootstrap;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.IntegrationTest;
|
||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.cloud.bootstrap.BootstrapDisabledAutoConfigurationIntegrationTests.Application;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringApplicationConfiguration(classes = Application.class)
|
||||
@IntegrationTest("spring.cloud.bootstrap.enabled:false")
|
||||
public class BootstrapDisabledAutoConfigurationIntegrationTests {
|
||||
|
||||
@Autowired
|
||||
private ConfigurableEnvironment environment;
|
||||
|
||||
@Test
|
||||
public void noBootstrapProperties() {
|
||||
assertFalse(environment.getPropertySources().contains("bootstrap"));
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@Configuration
|
||||
protected static class Application {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package org.springframework.cloud.bootstrap;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.IntegrationTest;
|
||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.cloud.bootstrap.BootstrapOrderingAutoConfigurationIntegrationTests.Application;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringApplicationConfiguration(classes = Application.class)
|
||||
@IntegrationTest("encrypt.key:deadbeef")
|
||||
@ActiveProfiles("encrypt")
|
||||
public class BootstrapOrderingAutoConfigurationIntegrationTests {
|
||||
|
||||
@Autowired
|
||||
private ConfigurableEnvironment environment;
|
||||
|
||||
@Test
|
||||
public void bootstrapPropertiesExist() {
|
||||
assertTrue(environment.getPropertySources().contains("bootstrap"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void normalPropertiesDecrypted() {
|
||||
assertEquals("foo", environment.resolvePlaceholders("${foo}"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bootstrapPropertiesDecrypted() {
|
||||
assertEquals("bar", environment.resolvePlaceholders("${bar}"));
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@Configuration
|
||||
protected static class Application {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,320 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2014 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.bootstrap.config;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNotSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.config.client.ConfigClientProperties;
|
||||
import org.springframework.cloud.config.client.PropertySourceLocator;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
import org.springframework.core.env.MutablePropertySources;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class BootstrapConfigurationTests {
|
||||
|
||||
private ConfigurableApplicationContext context;
|
||||
|
||||
@Rule
|
||||
public ExpectedException expected = ExpectedException.none();
|
||||
|
||||
@After
|
||||
public void close() {
|
||||
// Expected.* is bound to the PropertySourceConfiguration below
|
||||
System.clearProperty("expected.name");
|
||||
System.clearProperty("expected.fail");
|
||||
// Used to test system properties override
|
||||
System.clearProperty("bootstrap.foo");
|
||||
PropertySourceConfiguration.MAP.clear();
|
||||
if (context != null) {
|
||||
context.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pickupExternalBootstrapProperties() {
|
||||
String externalPropertiesPath = getExternalProperties();
|
||||
|
||||
context = new SpringApplicationBuilder().web(false)
|
||||
.sources(BareConfiguration.class)
|
||||
.properties("spring.cloud.bootstrap.location:" + externalPropertiesPath)
|
||||
.run();
|
||||
assertEquals("externalPropertiesInfoName",
|
||||
context.getEnvironment().getProperty("info.name"));
|
||||
assertTrue(context.getEnvironment().getPropertySources().contains("bootstrap"));
|
||||
assertNotNull(context.getBean(ConfigClientProperties.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* Running the test from maven will start from a different directory then starting it
|
||||
* from intellij
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private String getExternalProperties() {
|
||||
String externalPropertiesPath = "";
|
||||
File externalProperties = new File(
|
||||
"src/test/resources/external-properties/bootstrap.properties");
|
||||
if (externalProperties.exists()) {
|
||||
externalPropertiesPath = externalProperties.getAbsolutePath();
|
||||
}
|
||||
else {
|
||||
externalProperties = new File(
|
||||
"spring-cloud-config-client/src/test/resources/external-properties/bootstrap.properties");
|
||||
externalPropertiesPath = externalProperties.getAbsolutePath();
|
||||
}
|
||||
return externalPropertiesPath;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void picksUpAdditionalPropertySource() {
|
||||
PropertySourceConfiguration.MAP.put("bootstrap.foo", "bar");
|
||||
System.setProperty("expected.name", "bootstrap");
|
||||
context = new SpringApplicationBuilder().web(false)
|
||||
.sources(BareConfiguration.class).run();
|
||||
assertEquals("bar", context.getEnvironment().getProperty("bootstrap.foo"));
|
||||
assertTrue(context.getEnvironment().getPropertySources().contains("bootstrap"));
|
||||
assertNotNull(context.getBean(ConfigClientProperties.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void failsOnPropertySource() {
|
||||
System.setProperty("expected.fail", "true");
|
||||
expected.expectMessage("Planned");
|
||||
context = new SpringApplicationBuilder().web(false)
|
||||
.sources(BareConfiguration.class).run();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void overrideSystemPropertySourceByDefault() {
|
||||
PropertySourceConfiguration.MAP.put("bootstrap.foo", "bar");
|
||||
System.setProperty("bootstrap.foo", "system");
|
||||
context = new SpringApplicationBuilder().web(false)
|
||||
.sources(BareConfiguration.class).run();
|
||||
assertEquals("bar", context.getEnvironment().getProperty("bootstrap.foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void systemPropertyOverrideFalse() {
|
||||
PropertySourceConfiguration.MAP.put("bootstrap.foo", "bar");
|
||||
PropertySourceConfiguration.MAP.put(
|
||||
"spring.cloud.config.overrideSystemProperties", "false");
|
||||
System.setProperty("bootstrap.foo", "system");
|
||||
context = new SpringApplicationBuilder().web(false)
|
||||
.sources(BareConfiguration.class).run();
|
||||
assertEquals("system", context.getEnvironment().getProperty("bootstrap.foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void systemPropertyOverrideWhenOverrideDisallowed() {
|
||||
PropertySourceConfiguration.MAP.put("bootstrap.foo", "bar");
|
||||
PropertySourceConfiguration.MAP.put(
|
||||
"spring.cloud.config.overrideSystemProperties", "false");
|
||||
// If spring.cloud.config.allowOverride=false is in the remote property sources
|
||||
// with sufficiently high priority it always wins. Admins can enforce it by adding
|
||||
// their own remote property source.
|
||||
PropertySourceConfiguration.MAP.put("spring.cloud.config.allowOverride", "false");
|
||||
System.setProperty("bootstrap.foo", "system");
|
||||
context = new SpringApplicationBuilder().web(false)
|
||||
.sources(BareConfiguration.class).run();
|
||||
assertEquals("bar", context.getEnvironment().getProperty("bootstrap.foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void applicationNameInBootstrapAndMain() {
|
||||
System.setProperty("expected.name", "main");
|
||||
context = new SpringApplicationBuilder()
|
||||
.web(false)
|
||||
.properties("spring.cloud.bootstrap.name:other",
|
||||
"spring.config.name:plain").sources(BareConfiguration.class)
|
||||
.run();
|
||||
assertEquals("app",
|
||||
context.getEnvironment().getProperty("spring.application.name"));
|
||||
// The parent is called "main" because spring.application.name is specified in
|
||||
// other.properties (the bootstrap properties)
|
||||
assertEquals(
|
||||
"main",
|
||||
context.getParent().getEnvironment()
|
||||
.getProperty("spring.application.name"));
|
||||
// The bootstrap context has a different "bootstrap" property source
|
||||
assertNotSame(context.getEnvironment().getPropertySources().get("bootstrap"),
|
||||
((ConfigurableEnvironment) context.getParent().getEnvironment())
|
||||
.getPropertySources().get("bootstrap"));
|
||||
assertEquals("app", context.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void applicationNameNotInBootstrap() {
|
||||
System.setProperty("expected.name", "main");
|
||||
context = new SpringApplicationBuilder()
|
||||
.web(false)
|
||||
.properties("spring.cloud.bootstrap.name:application",
|
||||
"spring.config.name:other").sources(BareConfiguration.class)
|
||||
.run();
|
||||
assertEquals("main",
|
||||
context.getEnvironment().getProperty("spring.application.name"));
|
||||
// The parent is called "application" because spring.application.name is not
|
||||
// defined in the bootstrap properties
|
||||
assertEquals(
|
||||
"application",
|
||||
context.getParent().getEnvironment()
|
||||
.getProperty("spring.application.name"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void applicationNameOnlyInBootstrap() {
|
||||
System.setProperty("expected.name", "main");
|
||||
context = new SpringApplicationBuilder().web(false)
|
||||
.properties("spring.cloud.bootstrap.name:other")
|
||||
.sources(BareConfiguration.class).run();
|
||||
// The main context is called "main" because spring.application.name is specified
|
||||
// in other.properties (and not in the main config file)
|
||||
assertEquals("main",
|
||||
context.getEnvironment().getProperty("spring.application.name"));
|
||||
// The parent is called "main" because spring.application.name is specified in
|
||||
// other.properties (the bootstrap properties this time)
|
||||
assertEquals(
|
||||
"main",
|
||||
context.getParent().getEnvironment()
|
||||
.getProperty("spring.application.name"));
|
||||
assertEquals("main", context.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void environmentEnrichedOnceWhenSharedWithChildContext() {
|
||||
PropertySourceConfiguration.MAP.put("bootstrap.foo", "bar");
|
||||
context = new SpringApplicationBuilder().sources(BareConfiguration.class)
|
||||
.environment(new StandardEnvironment()).child(BareConfiguration.class)
|
||||
.web(false).run();
|
||||
assertEquals("bar", context.getEnvironment().getProperty("bootstrap.foo"));
|
||||
assertEquals(context.getEnvironment(), context.getParent().getEnvironment());
|
||||
MutablePropertySources sources = context.getEnvironment().getPropertySources();
|
||||
PropertySource<?> bootstrap = sources.get("bootstrap");
|
||||
assertNotNull(bootstrap);
|
||||
assertEquals(0, sources.precedenceOf(bootstrap));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void environmentEnrichedInParentContext() {
|
||||
PropertySourceConfiguration.MAP.put("bootstrap.foo", "bar");
|
||||
context = new SpringApplicationBuilder().sources(BareConfiguration.class)
|
||||
.child(BareConfiguration.class).web(false).run();
|
||||
assertEquals("bar", context.getEnvironment().getProperty("bootstrap.foo"));
|
||||
assertNotSame(context.getEnvironment(), context.getParent().getEnvironment());
|
||||
assertTrue(context.getEnvironment().getPropertySources().contains("bootstrap"));
|
||||
assertTrue(((ConfigurableEnvironment) context.getParent().getEnvironment())
|
||||
.getPropertySources().contains("bootstrap"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void differentProfileInChild() {
|
||||
PropertySourceConfiguration.MAP.put("bootstrap.foo", "bar");
|
||||
// Profiles are always merged with the child
|
||||
ConfigurableApplicationContext parent = new SpringApplicationBuilder()
|
||||
.sources(BareConfiguration.class).profiles("parent").web(false).run();
|
||||
context = new SpringApplicationBuilder(BareConfiguration.class).profiles("child")
|
||||
.parent(parent).web(false).run();
|
||||
assertNotSame(context.getEnvironment(), context.getParent().getEnvironment());
|
||||
// The ApplicationContext merges profiles (profiles and property sources), see
|
||||
// AbstractEnvironment.merge()
|
||||
assertTrue(this.context.getEnvironment().acceptsProfiles("child", "parent"));
|
||||
// But the parent is not a child
|
||||
assertFalse(this.context.getParent().getEnvironment().acceptsProfiles("child"));
|
||||
assertTrue(this.context.getParent().getEnvironment().acceptsProfiles("parent"));
|
||||
assertTrue(((ConfigurableEnvironment) context.getParent().getEnvironment())
|
||||
.getPropertySources().contains("bootstrap"));
|
||||
assertEquals("bar", context.getEnvironment().getProperty("bootstrap.foo"));
|
||||
// The "bootstrap" property source is not shared now, but it has the same
|
||||
// properties in it because they are pulled from the PropertySourceConfiguration
|
||||
// below
|
||||
assertEquals("bar",
|
||||
context.getParent().getEnvironment().getProperty("bootstrap.foo"));
|
||||
// The parent property source is there in the child because they are both in the
|
||||
// "parent" profile (by virtue of the merge in AbstractEnvironment)
|
||||
assertEquals("parent", context.getEnvironment().getProperty("info.name"));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(ConfigClientProperties.class)
|
||||
protected static class BareConfiguration {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConfigurationProperties("expected")
|
||||
// This is added to bootstrap context as a source in bootstrap.properties
|
||||
protected static class PropertySourceConfiguration implements PropertySourceLocator {
|
||||
|
||||
public static Map<String, Object> MAP = new HashMap<String, Object>(
|
||||
Collections.<String, Object> singletonMap("bootstrap.foo", "bar"));
|
||||
|
||||
private String name;
|
||||
|
||||
private boolean fail = false;
|
||||
|
||||
@Override
|
||||
public PropertySource<?> locate(Environment environment) {
|
||||
if (name != null) {
|
||||
assertEquals(name, environment.getProperty("spring.application.name"));
|
||||
}
|
||||
if (fail) {
|
||||
throw new RuntimeException("Planned");
|
||||
}
|
||||
return new MapPropertySource("testBootstrap", MAP);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public boolean isFail() {
|
||||
return fail;
|
||||
}
|
||||
|
||||
public void setFail(boolean fail) {
|
||||
this.fail = fail;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package org.springframework.cloud.bootstrap.encrypt;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.security.crypto.encrypt.TextEncryptor;
|
||||
|
||||
public class EncryptionBootstrapConfigurationTests {
|
||||
|
||||
@Test
|
||||
public void rsaKeyStore() {
|
||||
ConfigurableApplicationContext context = new SpringApplicationBuilder(
|
||||
EncryptionBootstrapConfiguration.class).web(false).properties(
|
||||
"encrypt.keyStore.location:classpath:/server.jks",
|
||||
"encrypt.keyStore.password:letmein",
|
||||
"encrypt.keyStore.alias:mytestkey", "encrypt.keyStore.secret:changeme")
|
||||
.run();
|
||||
TextEncryptor encryptor = context.getBean(TextEncryptor.class);
|
||||
assertEquals("foo", encryptor.decrypt(encryptor.encrypt("foo")));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2014 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.bootstrap.encrypt;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.boot.test.EnvironmentTestUtils;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.security.crypto.encrypt.Encryptors;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class EnvironmentDecryptApplicationListenerTests {
|
||||
|
||||
private EnvironmentDecryptApplicationInitializer listener = new EnvironmentDecryptApplicationInitializer(Encryptors.noOpText());
|
||||
|
||||
@Test
|
||||
public void decryptCipherKey() {
|
||||
ConfigurableApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
EnvironmentTestUtils.addEnvironment(context, "foo: {cipher}bar");
|
||||
listener.initialize(context);
|
||||
assertEquals("bar", context.getEnvironment().getProperty("foo"));
|
||||
}
|
||||
|
||||
@Test(expected=IllegalStateException.class)
|
||||
public void errorOnDecrypt() {
|
||||
listener = new EnvironmentDecryptApplicationInitializer(Encryptors.text("deadbeef", "AFFE37"));
|
||||
ConfigurableApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
EnvironmentTestUtils.addEnvironment(context, "foo: {cipher}bar");
|
||||
listener.initialize(context);
|
||||
assertEquals("bar", context.getEnvironment().getProperty("foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void errorOnDecryptWithEmpty() {
|
||||
listener = new EnvironmentDecryptApplicationInitializer(Encryptors.text("deadbeef", "AFFE37"));
|
||||
listener.setFailOnError(false);
|
||||
ConfigurableApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
EnvironmentTestUtils.addEnvironment(context, "foo: {cipher}bar");
|
||||
listener.initialize(context);
|
||||
// Empty is safest fallback for undecryptable cipher
|
||||
assertEquals("", context.getEnvironment().getProperty("foo"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
/*
|
||||
* Copyright 2006-2007 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.context.environment;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.cloud.context.environment.EnvironmentManagerIntegrationTests.TestConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
|
||||
@SpringApplicationConfiguration(classes = TestConfiguration.class)
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@WebAppConfiguration
|
||||
public class EnvironmentManagerIntegrationTests {
|
||||
|
||||
@Autowired
|
||||
private TestProperties properties;
|
||||
|
||||
@Autowired
|
||||
private WebApplicationContext context;
|
||||
|
||||
private MockMvc mvc;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
this.mvc = MockMvcBuilders.webAppContextSetup(this.context).build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRefresh() throws Exception {
|
||||
assertEquals("Hello scope!", properties.getMessage());
|
||||
// Change the dynamic property source...
|
||||
this.mvc.perform(post("/env").param("message", "Foo")).andExpect(status().isOk()).andExpect(
|
||||
content().string("{\"message\":\"Foo\"}"));
|
||||
assertEquals("Foo", properties.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRefreshFails() throws Exception {
|
||||
try {
|
||||
this.mvc.perform(post("/env").param("delay", "foo")).andExpect(
|
||||
status().is5xxServerError());
|
||||
fail("expected ServletException");
|
||||
} catch (ServletException e) {
|
||||
// The underlying BindException is not handled by the dispatcher servlet
|
||||
}
|
||||
assertEquals(0, properties.getDelay());
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(TestConfiguration.class, args);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
protected static class TestConfiguration {
|
||||
|
||||
@Bean
|
||||
protected TestProperties properties() {
|
||||
return new TestProperties();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ConfigurationProperties
|
||||
protected static class TestProperties {
|
||||
|
||||
private String message;
|
||||
|
||||
private int delay;
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public int getDelay() {
|
||||
return delay;
|
||||
}
|
||||
|
||||
public void setDelay(int delay) {
|
||||
this.delay = delay;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
/*
|
||||
* Copyright 2006-2007 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.context.properties;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.test.EnvironmentTestUtils;
|
||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.cloud.autoconfigure.RefreshAutoConfiguration;
|
||||
import org.springframework.cloud.context.properties.ConfigurationPropertiesRebinderIntegrationTests.TestConfiguration;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
@SpringApplicationConfiguration(classes=TestConfiguration.class)
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
public class ConfigurationPropertiesRebinderIntegrationTests {
|
||||
|
||||
@Autowired
|
||||
private TestProperties properties;
|
||||
|
||||
@Autowired
|
||||
private ConfigurationPropertiesRebinder rebinder;
|
||||
|
||||
@Autowired
|
||||
private ConfigurableEnvironment environment;
|
||||
|
||||
@Test
|
||||
@DirtiesContext
|
||||
public void testSimpleProperties() throws Exception {
|
||||
assertEquals("Hello scope!", properties.getMessage());
|
||||
// Change the dynamic property source...
|
||||
EnvironmentTestUtils.addEnvironment(environment, "message:Foo");
|
||||
// ...but don't refresh, so the bean stays the same:
|
||||
assertEquals("Hello scope!", properties.getMessage());
|
||||
assertEquals(1, properties.getCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DirtiesContext
|
||||
public void testRefresh() throws Exception {
|
||||
assertEquals(1, properties.getCount());
|
||||
assertEquals("Hello scope!", properties.getMessage());
|
||||
// Change the dynamic property source...
|
||||
EnvironmentTestUtils.addEnvironment(environment, "message:Foo");
|
||||
// ...and then refresh, so the bean is re-initialized:
|
||||
rebinder.rebind();
|
||||
assertEquals("Foo", properties.getMessage());
|
||||
assertEquals(2, properties.getCount());
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableConfigurationProperties
|
||||
@Import({RefreshAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class})
|
||||
protected static class TestConfiguration {
|
||||
|
||||
@Bean
|
||||
protected TestProperties properties() {
|
||||
return new TestProperties();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ConfigurationProperties
|
||||
protected static class TestProperties {
|
||||
private String message;
|
||||
private int delay;
|
||||
private int count = 0;
|
||||
public int getCount() {
|
||||
return count;
|
||||
}
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
public int getDelay() {
|
||||
return delay;
|
||||
}
|
||||
public void setDelay(int delay) {
|
||||
this.delay = delay;
|
||||
}
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
this.count ++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
* Copyright 2006-2007 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.context.restart;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNotSame;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
public class RestartIntegrationTests {
|
||||
|
||||
private ConfigurableApplicationContext context;
|
||||
|
||||
@After
|
||||
public void close() {
|
||||
if (context != null) {
|
||||
context.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRestartTwice() throws Exception {
|
||||
|
||||
context = SpringApplication.run(TestConfiguration.class, "--endpoints.restart.enabled=true", "--server.port=0");
|
||||
RestartEndpoint endpoint = context.getBean(RestartEndpoint.class);
|
||||
assertNotNull(context.getParent());
|
||||
assertNull(context.getParent().getParent());
|
||||
context = endpoint.restart();
|
||||
|
||||
assertNotNull(context);
|
||||
assertNotNull(context.getParent());
|
||||
assertNull(context.getParent().getParent());
|
||||
|
||||
RestartEndpoint next = context.getBean(RestartEndpoint.class);
|
||||
assertNotSame(endpoint, next);
|
||||
context = next.restart();
|
||||
|
||||
assertNotNull(context);
|
||||
assertNotNull(context.getParent());
|
||||
assertNull(context.getParent().getParent());
|
||||
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(TestConfiguration.class, args);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
protected static class TestConfiguration {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* Copyright 2006-2007 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.context.scope.refresh;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.cloud.autoconfigure.RefreshAutoConfiguration;
|
||||
import org.springframework.cloud.context.config.annotation.RefreshScope;
|
||||
import org.springframework.cloud.context.scope.refresh.ImportRefreshScopeIntegrationTests.TestConfiguration;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
@SpringApplicationConfiguration(classes = TestConfiguration.class)
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
public class ImportRefreshScopeIntegrationTests {
|
||||
|
||||
@Autowired
|
||||
private ConfigurableListableBeanFactory beanFactory;
|
||||
|
||||
@Autowired
|
||||
private ExampleService service;
|
||||
|
||||
@Autowired
|
||||
private org.springframework.cloud.context.scope.refresh.RefreshScope scope;
|
||||
|
||||
@Test
|
||||
public void testSimpleProperties() throws Exception {
|
||||
assertEquals("Hello scope!", service.getMessage());
|
||||
assertEquals("refresh", beanFactory.getBeanDefinition("scopedTarget.service").getScope());
|
||||
assertEquals("Hello scope!", service.getMessage());
|
||||
}
|
||||
|
||||
@Configuration("service")
|
||||
@RefreshScope
|
||||
public static class ExampleService {
|
||||
|
||||
public String getMessage() {
|
||||
return "Hello scope!";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import({ RefreshAutoConfiguration.class, ExampleService.class })
|
||||
protected static class TestConfiguration {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,230 +0,0 @@
|
||||
/*
|
||||
* Copyright 2006-2007 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.context.scope.refresh;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.aop.framework.Advised;
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.test.EnvironmentTestUtils;
|
||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.jmx.export.annotation.ManagedAttribute;
|
||||
import org.springframework.jmx.export.annotation.ManagedResource;
|
||||
import org.springframework.cloud.autoconfigure.RefreshAutoConfiguration;
|
||||
import org.springframework.cloud.context.config.annotation.RefreshScope;
|
||||
import org.springframework.cloud.context.scope.refresh.MoreRefreshScopeIntegrationTests.TestConfiguration;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
@SpringApplicationConfiguration(classes = TestConfiguration.class)
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
public class MoreRefreshScopeIntegrationTests {
|
||||
|
||||
@Autowired
|
||||
private TestService service;
|
||||
|
||||
@Autowired
|
||||
private TestProperties properties;
|
||||
|
||||
@Autowired
|
||||
private org.springframework.cloud.context.scope.refresh.RefreshScope scope;
|
||||
|
||||
@Autowired
|
||||
private ConfigurableEnvironment environment;
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
TestService.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DirtiesContext
|
||||
public void testSimpleProperties() throws Exception {
|
||||
assertEquals("Hello scope!", service.getMessage());
|
||||
assertTrue(service instanceof Advised);
|
||||
// Change the dynamic property source...
|
||||
EnvironmentTestUtils.addEnvironment(environment, "message:Foo");
|
||||
// ...but don't refresh, so the bean stays the same:
|
||||
assertEquals("Hello scope!", service.getMessage());
|
||||
assertEquals(1, TestService.getInitCount());
|
||||
assertEquals(0, TestService.getDestroyCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DirtiesContext
|
||||
public void testRefresh() throws Exception {
|
||||
assertEquals("Hello scope!", service.getMessage());
|
||||
String id1 = service.toString();
|
||||
// Change the dynamic property source...
|
||||
EnvironmentTestUtils.addEnvironment(environment, "message:Foo");
|
||||
// ...and then refresh, so the bean is re-initialized:
|
||||
scope.refreshAll();
|
||||
String id2 = service.toString();
|
||||
assertEquals("Foo", service.getMessage());
|
||||
assertEquals(2, TestService.getInitCount());
|
||||
assertEquals(1, TestService.getDestroyCount());
|
||||
assertNotSame(id1, id2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DirtiesContext
|
||||
public void testRefreshFails() throws Exception {
|
||||
assertEquals("Hello scope!", service.getMessage());
|
||||
// Change the dynamic property source...
|
||||
EnvironmentTestUtils.addEnvironment(environment, "message:Foo", "delay:foo");
|
||||
// ...and then refresh, so the bean is re-initialized:
|
||||
scope.refreshAll();
|
||||
try {
|
||||
// If a refresh fails (e.g. a binding error in this case) the application is
|
||||
// basically hosed.
|
||||
assertEquals("Hello scope!", service.getMessage());
|
||||
fail("expected BeanCreationException");
|
||||
} catch (BeanCreationException e) {
|
||||
}
|
||||
// But we can fix it by fixing the binding error:
|
||||
EnvironmentTestUtils.addEnvironment(environment, "delay:0");
|
||||
// ...and then refresh, so the bean is re-initialized:
|
||||
scope.refreshAll();
|
||||
assertEquals("Foo", service.getMessage());
|
||||
}
|
||||
|
||||
public static class TestService implements InitializingBean, DisposableBean {
|
||||
|
||||
private static Log logger = LogFactory.getLog(TestService.class);
|
||||
|
||||
private volatile static int initCount = 0;
|
||||
|
||||
private volatile static int destroyCount = 0;
|
||||
|
||||
private String message = null;
|
||||
|
||||
private volatile long delay = 0;
|
||||
|
||||
public void setDelay(long delay) {
|
||||
this.delay = delay;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
logger.debug("Initializing message: " + message);
|
||||
initCount++;
|
||||
}
|
||||
|
||||
public void destroy() throws Exception {
|
||||
logger.debug("Destroying message: " + message);
|
||||
destroyCount++;
|
||||
message = null;
|
||||
}
|
||||
|
||||
public static void reset() {
|
||||
initCount = 0;
|
||||
destroyCount = 0;
|
||||
}
|
||||
|
||||
public static int getInitCount() {
|
||||
return initCount;
|
||||
}
|
||||
|
||||
public static int getDestroyCount() {
|
||||
return destroyCount;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
logger.debug("Setting message: " + message);
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
logger.debug("Getting message: " + message);
|
||||
try {
|
||||
Thread.sleep(delay);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
logger.info("Returning message: " + message);
|
||||
return message;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableConfigurationProperties
|
||||
@Import({ RefreshAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class })
|
||||
protected static class TestConfiguration {
|
||||
|
||||
@Bean
|
||||
@RefreshScope
|
||||
protected TestProperties properties() {
|
||||
return new TestProperties();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@RefreshScope
|
||||
public TestService service() {
|
||||
TestService service = new TestService();
|
||||
service.setMessage(properties().getMessage());
|
||||
service.setDelay(properties().getDelay());
|
||||
return service;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ConfigurationProperties
|
||||
@ManagedResource
|
||||
protected static class TestProperties {
|
||||
|
||||
private String message;
|
||||
|
||||
private int delay;
|
||||
|
||||
@ManagedAttribute
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@ManagedAttribute
|
||||
public int getDelay() {
|
||||
return delay;
|
||||
}
|
||||
|
||||
public void setDelay(int delay) {
|
||||
this.delay = delay;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2014 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.context.scope.refresh;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.IntegrationTest;
|
||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.boot.test.TestRestTemplate;
|
||||
import org.springframework.cloud.context.config.annotation.RefreshScope;
|
||||
import org.springframework.cloud.context.scope.refresh.RefreshEndpointIntegrationTests.ClientApp;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.RequestEntity;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringApplicationConfiguration(classes = ClientApp.class)
|
||||
@IntegrationTest("server.port:0")
|
||||
@WebAppConfiguration
|
||||
public class RefreshEndpointIntegrationTests {
|
||||
|
||||
@Value("${local.server.port}")
|
||||
private int port;
|
||||
|
||||
@Test
|
||||
public void webAccess() throws Exception {
|
||||
TestRestTemplate template = new TestRestTemplate();
|
||||
template.exchange(
|
||||
getUrlEncodedEntity("http://localhost:" + port + "/env", "message",
|
||||
"Hello Dave!"), String.class);
|
||||
template.postForObject("http://localhost:" + port + "/refresh", "", String.class);
|
||||
String message = template.getForObject("http://localhost:" + port + "/",
|
||||
String.class);
|
||||
assertEquals("Hello Dave!", message);
|
||||
}
|
||||
|
||||
private RequestEntity<?> getUrlEncodedEntity(String uri, String key, String value)
|
||||
throws URISyntaxException {
|
||||
MultiValueMap<String, String> env = new LinkedMultiValueMap<String, String>(
|
||||
Collections.singletonMap("message", Arrays.asList("Hello Dave!")));
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
|
||||
RequestEntity<MultiValueMap<String, String>> entity = new RequestEntity<MultiValueMap<String, String>>(
|
||||
env, headers, HttpMethod.POST, new URI(uri));
|
||||
return entity;
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
protected static class ClientApp {
|
||||
|
||||
@Bean
|
||||
@RefreshScope
|
||||
public Controller controller() {
|
||||
return new Controller();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ClientApp.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@RestController
|
||||
protected static class Controller {
|
||||
|
||||
@Value("${message:Hello World!}")
|
||||
String message;
|
||||
|
||||
@RequestMapping("/")
|
||||
public String hello() {
|
||||
return message;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,185 +0,0 @@
|
||||
/*
|
||||
* Copyright 2006-2007 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.context.scope.refresh;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.jmx.export.annotation.ManagedAttribute;
|
||||
import org.springframework.jmx.export.annotation.ManagedResource;
|
||||
import org.springframework.cloud.autoconfigure.RefreshAutoConfiguration;
|
||||
import org.springframework.cloud.context.config.annotation.RefreshScope;
|
||||
import org.springframework.cloud.context.scope.refresh.RefreshScopeConcurrencyTests.TestConfiguration;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.annotation.Repeat;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
@SpringApplicationConfiguration(classes=TestConfiguration.class)
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
public class RefreshScopeConcurrencyTests {
|
||||
|
||||
private static Log logger = LogFactory.getLog(RefreshScopeConcurrencyTests.class);
|
||||
|
||||
private ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||
|
||||
@Autowired
|
||||
private Service service;
|
||||
|
||||
@Autowired
|
||||
private TestProperties properties;
|
||||
|
||||
@Autowired
|
||||
private org.springframework.cloud.context.scope.refresh.RefreshScope scope;
|
||||
|
||||
@Test
|
||||
@Repeat(10)
|
||||
@DirtiesContext
|
||||
public void testConcurrentRefresh() throws Exception {
|
||||
|
||||
assertEquals("Hello scope!", service.getMessage());
|
||||
properties.setMessage("Foo");
|
||||
properties.setDelay(500);
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
Future<String> result = executor.submit(new Callable<String>() {
|
||||
public String call() throws Exception {
|
||||
logger.debug("Background started.");
|
||||
try {
|
||||
return service.getMessage();
|
||||
} finally {
|
||||
latch.countDown();
|
||||
logger.debug("Background done.");
|
||||
}
|
||||
}
|
||||
});
|
||||
assertTrue(latch.await(1500, TimeUnit.MILLISECONDS));
|
||||
logger.info("Refreshing");
|
||||
scope.refreshAll();
|
||||
assertEquals("Foo", service.getMessage());
|
||||
/*
|
||||
* This is the most important assertion: we don't want a null value because that means the bean was destroyed
|
||||
* and not re-initialized before we accessed it.
|
||||
*/
|
||||
assertNotNull(result.get());
|
||||
assertEquals("Hello scope!", result.get());
|
||||
}
|
||||
|
||||
public static interface Service {
|
||||
|
||||
String getMessage();
|
||||
|
||||
}
|
||||
|
||||
public static class ExampleService implements Service, InitializingBean, DisposableBean {
|
||||
|
||||
private static Log logger = LogFactory.getLog(ExampleService.class);
|
||||
|
||||
private String message = null;
|
||||
private volatile long delay = 0;
|
||||
|
||||
public void setDelay(long delay) {
|
||||
this.delay = delay;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
logger.debug("Initializing message: " + message);
|
||||
}
|
||||
|
||||
public void destroy() throws Exception {
|
||||
logger.debug("Destroying message: " + message);
|
||||
message = null;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
logger.debug("Setting message: " + message);
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
logger.debug("Getting message: " + message);
|
||||
try {
|
||||
Thread.sleep(delay);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
logger.info("Returning message: " + message);
|
||||
return message;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(TestProperties.class)
|
||||
@Import({RefreshAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class})
|
||||
protected static class TestConfiguration {
|
||||
|
||||
@Autowired
|
||||
private TestProperties properties;
|
||||
|
||||
@Bean
|
||||
@RefreshScope
|
||||
public ExampleService service() {
|
||||
ExampleService service = new ExampleService();
|
||||
service.setMessage(properties.getMessage());
|
||||
service.setDelay(properties.getDelay());
|
||||
return service;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ConfigurationProperties
|
||||
@ManagedResource
|
||||
protected static class TestProperties {
|
||||
private String message;
|
||||
private int delay;
|
||||
@ManagedAttribute
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
@ManagedAttribute
|
||||
public int getDelay() {
|
||||
return delay;
|
||||
}
|
||||
public void setDelay(int delay) {
|
||||
this.delay = delay;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,169 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2014 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.context.scope.refresh;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
|
||||
import org.springframework.cloud.autoconfigure.RefreshAutoConfiguration;
|
||||
import org.springframework.cloud.context.config.annotation.RefreshScope;
|
||||
import org.springframework.cloud.context.environment.EnvironmentManager;
|
||||
import org.springframework.cloud.context.scope.refresh.RefreshScopeConfigurationTests.NestedApp.NestedController;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class RefreshScopeConfigurationTests {
|
||||
|
||||
private AnnotationConfigApplicationContext context;
|
||||
|
||||
@Rule
|
||||
public ExpectedException expected = ExpectedException.none();
|
||||
|
||||
@After
|
||||
public void init() {
|
||||
if (context!=null) {
|
||||
context.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void refresh() {
|
||||
EnvironmentManager environmentManager = context.getBean(EnvironmentManager.class);
|
||||
environmentManager.setProperty("message", "Hello Dave!");
|
||||
org.springframework.cloud.context.scope.refresh.RefreshScope scope = context.getBean(org.springframework.cloud.context.scope.refresh.RefreshScope.class);
|
||||
scope.refreshAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* See gh-43
|
||||
*/
|
||||
@Test
|
||||
public void configurationWithRefreshScope() throws Exception {
|
||||
context = new AnnotationConfigApplicationContext(Application.class,
|
||||
PropertyPlaceholderAutoConfiguration.class, RefreshAutoConfiguration.class);
|
||||
Application application = context.getBean(Application.class);
|
||||
assertEquals("refresh", context.getBeanDefinition("scopedTarget.application").getScope());
|
||||
application.hello();
|
||||
refresh();
|
||||
String message = application.hello();
|
||||
assertEquals("Hello Dave!", message);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void refreshScopeOnBean() throws Exception {
|
||||
context = new AnnotationConfigApplicationContext(ClientApp.class,
|
||||
PropertyPlaceholderAutoConfiguration.class, RefreshAutoConfiguration.class);
|
||||
Controller application = context.getBean(Controller.class);
|
||||
application.hello();
|
||||
refresh();
|
||||
String message = application.hello();
|
||||
assertEquals("Hello Dave!", message);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void refreshScopeOnNested() throws Exception {
|
||||
context = new AnnotationConfigApplicationContext(NestedApp.class,
|
||||
PropertyPlaceholderAutoConfiguration.class, RefreshAutoConfiguration.class);
|
||||
NestedController application = context.getBean(NestedController.class);
|
||||
application.hello();
|
||||
refresh();
|
||||
String message = application.hello();
|
||||
assertEquals("Hello Dave!", message);
|
||||
}
|
||||
|
||||
// WTF? Maven can't compile without the FQN on this one (not the others).
|
||||
@org.springframework.context.annotation.Configuration
|
||||
protected static class NestedApp {
|
||||
|
||||
@RestController
|
||||
@RefreshScope
|
||||
protected static class NestedController {
|
||||
|
||||
@Value("${message:Hello World!}")
|
||||
String message;
|
||||
|
||||
@RequestMapping("/")
|
||||
public String hello() {
|
||||
return message;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ClientApp.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration("application")
|
||||
@RefreshScope
|
||||
protected static class Application {
|
||||
|
||||
@Value("${message:Hello World!}")
|
||||
String message = "Hello World";
|
||||
|
||||
@RequestMapping("/")
|
||||
public String hello() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Application.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
protected static class ClientApp {
|
||||
|
||||
@Bean
|
||||
@RefreshScope
|
||||
public Controller controller() {
|
||||
return new Controller();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ClientApp.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@RestController
|
||||
protected static class Controller {
|
||||
|
||||
@Value("${message:Hello World!}")
|
||||
String message;
|
||||
|
||||
@RequestMapping("/")
|
||||
public String hello() {
|
||||
return message;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,229 +0,0 @@
|
||||
/*
|
||||
* Copyright 2006-2007 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.context.scope.refresh;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.aop.framework.Advised;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.cloud.autoconfigure.RefreshAutoConfiguration;
|
||||
import org.springframework.cloud.context.config.annotation.RefreshScope;
|
||||
import org.springframework.cloud.context.scope.GenericScope;
|
||||
import org.springframework.cloud.context.scope.refresh.RefreshScopeIntegrationTests.TestConfiguration;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.jmx.export.annotation.ManagedAttribute;
|
||||
import org.springframework.jmx.export.annotation.ManagedResource;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
@SpringApplicationConfiguration(classes = TestConfiguration.class)
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
public class RefreshScopeIntegrationTests {
|
||||
|
||||
@Autowired
|
||||
private Service service;
|
||||
|
||||
@Autowired
|
||||
private TestProperties properties;
|
||||
|
||||
@Autowired
|
||||
private org.springframework.cloud.context.scope.refresh.RefreshScope scope;
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
ExampleService.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DirtiesContext
|
||||
public void testSimpleProperties() throws Exception {
|
||||
assertEquals("Hello scope!", service.getMessage());
|
||||
assertTrue(service instanceof Advised);
|
||||
// Change the dynamic property source...
|
||||
properties.setMessage("Foo");
|
||||
// ...but don't refresh, so the bean stays the same:
|
||||
assertEquals("Hello scope!", service.getMessage());
|
||||
assertEquals(1, ExampleService.getInitCount());
|
||||
assertEquals(0, ExampleService.getDestroyCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DirtiesContext
|
||||
public void testRefresh() throws Exception {
|
||||
assertEquals("Hello scope!", service.getMessage());
|
||||
String id1 = service.toString();
|
||||
// Change the dynamic property source...
|
||||
properties.setMessage("Foo");
|
||||
// ...and then refresh, so the bean is re-initialized:
|
||||
scope.refreshAll();
|
||||
String id2 = service.toString();
|
||||
assertEquals("Foo", service.getMessage());
|
||||
assertEquals(2, ExampleService.getInitCount());
|
||||
assertEquals(1, ExampleService.getDestroyCount());
|
||||
assertNotSame(id1, id2);
|
||||
assertNotNull(ExampleService.event);
|
||||
assertEquals(RefreshScopeRefreshedEvent.DEFAULT_NAME,
|
||||
ExampleService.event.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DirtiesContext
|
||||
public void testRefreshBean() throws Exception {
|
||||
assertEquals("Hello scope!", service.getMessage());
|
||||
String id1 = service.toString();
|
||||
// Change the dynamic property source...
|
||||
properties.setMessage("Foo");
|
||||
// ...and then refresh, so the bean is re-initialized:
|
||||
scope.refresh("service");
|
||||
String id2 = service.toString();
|
||||
assertEquals("Foo", service.getMessage());
|
||||
assertEquals(2, ExampleService.getInitCount());
|
||||
assertEquals(1, ExampleService.getDestroyCount());
|
||||
assertNotSame(id1, id2);
|
||||
assertNotNull(ExampleService.event);
|
||||
assertEquals(GenericScope.SCOPED_TARGET_PREFIX + "service",
|
||||
ExampleService.event.getName());
|
||||
}
|
||||
|
||||
public static interface Service {
|
||||
|
||||
String getMessage();
|
||||
|
||||
}
|
||||
|
||||
public static class ExampleService implements Service, InitializingBean,
|
||||
DisposableBean, ApplicationListener<RefreshScopeRefreshedEvent> {
|
||||
|
||||
private static Log logger = LogFactory.getLog(ExampleService.class);
|
||||
|
||||
private volatile static int initCount = 0;
|
||||
private volatile static int destroyCount = 0;
|
||||
private volatile static RefreshScopeRefreshedEvent event;
|
||||
|
||||
private String message = null;
|
||||
private volatile long delay = 0;
|
||||
|
||||
public void setDelay(long delay) {
|
||||
this.delay = delay;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
logger.debug("Initializing message: " + message);
|
||||
initCount++;
|
||||
}
|
||||
|
||||
public void destroy() throws Exception {
|
||||
logger.debug("Destroying message: " + message);
|
||||
destroyCount++;
|
||||
message = null;
|
||||
}
|
||||
|
||||
public static void reset() {
|
||||
initCount = 0;
|
||||
destroyCount = 0;
|
||||
event = null;
|
||||
}
|
||||
|
||||
public static int getInitCount() {
|
||||
return initCount;
|
||||
}
|
||||
|
||||
public static int getDestroyCount() {
|
||||
return destroyCount;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
logger.debug("Setting message: " + message);
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
logger.debug("Getting message: " + message);
|
||||
try {
|
||||
Thread.sleep(delay);
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
logger.info("Returning message: " + message);
|
||||
return message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(RefreshScopeRefreshedEvent e) {
|
||||
event = e;
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(TestProperties.class)
|
||||
@Import({ RefreshAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class })
|
||||
protected static class TestConfiguration {
|
||||
|
||||
@Autowired
|
||||
private TestProperties properties;
|
||||
|
||||
@Bean
|
||||
@RefreshScope
|
||||
public ExampleService service() {
|
||||
ExampleService service = new ExampleService();
|
||||
service.setMessage(properties.getMessage());
|
||||
service.setDelay(properties.getDelay());
|
||||
return service;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ConfigurationProperties
|
||||
@ManagedResource
|
||||
protected static class TestProperties {
|
||||
private String message;
|
||||
private int delay;
|
||||
|
||||
@ManagedAttribute
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@ManagedAttribute
|
||||
public int getDelay() {
|
||||
return delay;
|
||||
}
|
||||
|
||||
public void setDelay(int delay) {
|
||||
this.delay = delay;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2014 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.context.scope.refresh;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.cloud.context.config.annotation.RefreshScope;
|
||||
import org.springframework.cloud.context.environment.EnvironmentManager;
|
||||
import org.springframework.cloud.context.scope.refresh.RefreshScopeWebIntegrationTests.Application;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringApplicationConfiguration(classes = Application.class)
|
||||
public class RefreshScopeWebIntegrationTests {
|
||||
|
||||
@Autowired
|
||||
private org.springframework.cloud.context.scope.refresh.RefreshScope scope;
|
||||
|
||||
@Autowired
|
||||
private EnvironmentManager environmentManager;
|
||||
|
||||
@Autowired
|
||||
private Client application;
|
||||
|
||||
@Autowired
|
||||
private ConfigurableListableBeanFactory beanFactory;
|
||||
|
||||
@Test
|
||||
public void scopeOnBeanDefinition() throws Exception {
|
||||
assertEquals("refresh", beanFactory.getBeanDefinition("scopedTarget.application").getScope());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void beanAccess() throws Exception {
|
||||
application.hello();
|
||||
environmentManager.setProperty("message", "Hello Dave!");
|
||||
scope.refreshAll();
|
||||
String message = application.hello();
|
||||
assertEquals("Hello Dave!", message);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
protected static class Application {
|
||||
|
||||
@Bean
|
||||
@RefreshScope
|
||||
public Client application() {
|
||||
return new Client();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Application.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@RestController
|
||||
protected static class Client {
|
||||
|
||||
@Value("${message:Hello World!}")
|
||||
String message;
|
||||
|
||||
@RequestMapping("/")
|
||||
public String hello() {
|
||||
return message;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2014 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.logging;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.boot.logging.LogLevel;
|
||||
import org.springframework.boot.logging.LoggingSystem;
|
||||
import org.springframework.boot.test.EnvironmentTestUtils;
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class LoggingRebinderTests {
|
||||
|
||||
private LoggingRebinder rebinder = new LoggingRebinder();
|
||||
private Logger logger = LoggerFactory.getLogger("org.springframework.web");
|
||||
|
||||
@After
|
||||
public void reset() {
|
||||
LoggingSystem.get(getClass().getClassLoader()).setLogLevel("org.springframework.web", LogLevel.INFO);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void logLevelsChanged() {
|
||||
assertFalse(logger.isTraceEnabled());
|
||||
StandardEnvironment environment = new StandardEnvironment();
|
||||
EnvironmentTestUtils.addEnvironment(environment, "logging.level.org.springframework.web=TRACE");
|
||||
rebinder.setEnvironment(environment);
|
||||
rebinder.onApplicationEvent(new EnvironmentChangeEvent(Collections.singleton("logging.level.org.springframework.web")));
|
||||
assertTrue(logger.isTraceEnabled());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
foo: {cipher}e4e061f9fe39ba5b14d8012d2f17d39775606039409b71ed4be0fdd033d5324a
|
||||
@@ -1,5 +0,0 @@
|
||||
message: Hello scope!
|
||||
delay: 0
|
||||
debug: true
|
||||
#logging.level.org.springframework.web: DEBUG
|
||||
#logging.level.org.springframework.context.annotation: DEBUG
|
||||
@@ -1 +0,0 @@
|
||||
bar: {cipher}6154ca04d4bb6144d672c4e3d750b5147116dd381946d51fa44f8bc25dc256f4
|
||||
@@ -1 +0,0 @@
|
||||
info.name: parent
|
||||
@@ -1,2 +0,0 @@
|
||||
spring.main.sources: org.springframework.cloud.bootstrap.config.BootstrapConfigurationTests.PropertySourceConfiguration
|
||||
info.name: child
|
||||
@@ -1 +0,0 @@
|
||||
info.name: externalPropertiesInfoName
|
||||
@@ -1 +0,0 @@
|
||||
spring.application.name: main
|
||||
@@ -1 +0,0 @@
|
||||
spring.application.name: app
|
||||
Binary file not shown.
@@ -29,10 +29,10 @@ import java.util.Map.Entry;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cloud.config.encrypt.EncryptorFactory;
|
||||
import org.springframework.cloud.config.encrypt.KeyFormatException;
|
||||
import org.springframework.cloud.config.environment.Environment;
|
||||
import org.springframework.cloud.config.environment.PropertySource;
|
||||
import org.springframework.cloud.context.encrypt.EncryptorFactory;
|
||||
import org.springframework.cloud.context.encrypt.KeyFormatException;
|
||||
import org.springframework.core.io.ByteArrayResource;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
|
||||
@@ -17,7 +17,7 @@ package org.springframework.cloud.config.server;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.config.client.PropertySourceLocator;
|
||||
import org.springframework.cloud.bootstrap.config.PropertySourceLocator;
|
||||
import org.springframework.cloud.config.environment.PropertySource;
|
||||
import org.springframework.core.env.CompositePropertySource;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
@@ -22,12 +22,12 @@ import static org.junit.Assert.assertTrue;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.cloud.config.encrypt.KeyFormatException;
|
||||
import org.springframework.cloud.config.environment.Environment;
|
||||
import org.springframework.cloud.config.environment.PropertySource;
|
||||
import org.springframework.cloud.config.server.EncryptionController;
|
||||
import org.springframework.cloud.config.server.InvalidCipherException;
|
||||
import org.springframework.cloud.config.server.KeyNotInstalledException;
|
||||
import org.springframework.cloud.context.encrypt.KeyFormatException;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.rsa.crypto.RsaSecretEncryptor;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user