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:
Dave Syer
2015-03-18 15:41:20 +00:00
parent cab00b3edd
commit 02b13a41b2
68 changed files with 740 additions and 5201 deletions

View File

@@ -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>

View File

@@ -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>

View File

@@ -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();
}
}

View File

@@ -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;
}
}
}
}

View File

@@ -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);
}
}
}
}

View File

@@ -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 {};
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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?");
}
}
}

View File

@@ -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);
}
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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 {
}
}

View File

@@ -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;
}
}

View File

@@ -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 {
}

View File

@@ -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;
}
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}
}
}

View File

@@ -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());
}
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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>&lt;aop:auto-proxy/&gt;</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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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());
}
}

View File

@@ -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();
}
}

View File

@@ -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 + "'");
}
}
}

View File

@@ -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

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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 {
}
}

View File

@@ -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 {
}
}

View File

@@ -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;
}
}
}

View File

@@ -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")));
}
}

View File

@@ -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"));
}
}

View File

@@ -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;
}
}
}

View File

@@ -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 ++;
}
}
}

View File

@@ -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 {
}
}

View File

@@ -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 {
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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());
}
}

View File

@@ -1 +0,0 @@
foo: {cipher}e4e061f9fe39ba5b14d8012d2f17d39775606039409b71ed4be0fdd033d5324a

View File

@@ -1,5 +0,0 @@
message: Hello scope!
delay: 0
debug: true
#logging.level.org.springframework.web: DEBUG
#logging.level.org.springframework.context.annotation: DEBUG

View File

@@ -1 +0,0 @@
bar: {cipher}6154ca04d4bb6144d672c4e3d750b5147116dd381946d51fa44f8bc25dc256f4

View File

@@ -1,2 +0,0 @@
spring.main.sources: org.springframework.cloud.bootstrap.config.BootstrapConfigurationTests.PropertySourceConfiguration
info.name: child

View File

@@ -1 +0,0 @@
info.name: externalPropertiesInfoName

View File

@@ -1 +0,0 @@
spring.application.name: main

View File

@@ -1 +0,0 @@
spring.application.name: app

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;