Initial commit
This commit is contained in:
11
.gitignore
vendored
Normal file
11
.gitignore
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
*~
|
||||
.#*
|
||||
*#
|
||||
target
|
||||
bin
|
||||
.classpath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.DS_Store
|
||||
*.sw*
|
||||
85
pom.xml
Normal file
85
pom.xml
Normal file
@@ -0,0 +1,85 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.springframework.platform</groupId>
|
||||
<artifactId>spring-platform-config</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>Spring Platform Config</name>
|
||||
<description>Spring Platform Config</description>
|
||||
<url>http://projects.spring.io/spring-platform/</url>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>1.1.2.BUILD-SNAPSHOT</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<organization>
|
||||
<name>Pivotal Software, Inc.</name>
|
||||
<url>http://www.spring.io</url>
|
||||
</organization>
|
||||
<licenses>
|
||||
<license>
|
||||
<name>Apache License, Version 2.0</name>
|
||||
<url>http://www.apache.org/licenses/LICENSE-2.0</url>
|
||||
</license>
|
||||
</licenses>
|
||||
<scm>
|
||||
<url>https://github.com/spring-platform/spring-platform-config</url>
|
||||
</scm>
|
||||
<developers>
|
||||
<developer>
|
||||
<id>dsyer</id>
|
||||
<name>Dave Syer</name>
|
||||
<email>dsyer at gopivotal.com</email>
|
||||
<organization>Pivotal Software, Inc.</organization>
|
||||
<organizationUrl>http://www.spring.io</organizationUrl>
|
||||
<roles>
|
||||
<role>Project lead</role>
|
||||
</roles>
|
||||
</developer>
|
||||
</developers>
|
||||
<prerequisites>
|
||||
<maven>3.0.0</maven>
|
||||
</prerequisites>
|
||||
<properties>
|
||||
<main.basedir>${basedir}</main.basedir>
|
||||
</properties>
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-antrun-plugin</artifactId>
|
||||
<version>1.7</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.17</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>com.google.code.maven-replacer-plugin</groupId>
|
||||
<artifactId>replacer</artifactId>
|
||||
<version>1.5.3</version>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>default</id>
|
||||
<activation>
|
||||
<activeByDefault>true</activeByDefault>
|
||||
</activation>
|
||||
<modules>
|
||||
<module>spring-platform-config-client</module>
|
||||
<module>spring-platform-config-server</module>
|
||||
</modules>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>full</id>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
126
spring-platform-config-client/README.md
Normal file
126
spring-platform-config-client/README.md
Normal file
@@ -0,0 +1,126 @@
|
||||
# Spring Platform Components
|
||||
|
||||
This project contains boilerplate patterns for distributed application
|
||||
components, making those patterns easy to implement with Spring (and
|
||||
Spring Boot in particular).
|
||||
|
||||
## Spring Boot Features
|
||||
|
||||
**Bootstrap Listener** - A `SpringApplication` with the platform
|
||||
components on its classpath will have an extended bootstrap phase,
|
||||
allowing new strategies for configuration management and other
|
||||
non-functional features to be added to the application before it is
|
||||
initialized. The implementation is similar to
|
||||
`@EnableAutoConfiguration`, allowing new jars to contribute additional
|
||||
features by defining `BootstrapConfiguration` classes in the form of
|
||||
`@Configuration`. Instead of being added to the application's context
|
||||
they are added to a special "bootstrap" context, which is then used to
|
||||
extract `ApplicationContextInitializers` to apply to the application
|
||||
before it is created. A typical use case would be to locate and
|
||||
install additional environment properties so that the application can
|
||||
adapt to its environment in a completely flexible way.
|
||||
|
||||
**Autoconfiguration** - An application using
|
||||
`@EnableAutoConfiguration` will pick up beans for the components
|
||||
below, notably `EnvironmentManager` and `RefreshScope`.
|
||||
|
||||
## EnvironmentManager and EnvironmentManagerEndpoint
|
||||
|
||||
The `EnvironmentManager` allows you to modify the Spring `Environment`
|
||||
in a running application. It adds a `PropertySource` with high
|
||||
priority to the `Environment` (so properties set there will take
|
||||
precedence). If you have the Actuator and are in a webapp then the
|
||||
`EnvironmentManagerEndpoint` will also expose "/env" with POST for
|
||||
updating the `EnvironmentManager` remotely. E.g. run the app locally:
|
||||
|
||||
```
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
public class Application {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Application.class, args);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
then interact with the `Environment`:
|
||||
|
||||
```
|
||||
$ curl localhost:8080/env/message
|
||||
Hello
|
||||
$ curl localhost:8080/env -d message=Foo
|
||||
$ curl localhost:8080/env/message
|
||||
Foo
|
||||
```
|
||||
|
||||
Doing this (or the equivalent operation over JMX) will trigger an event
|
||||
that leads to rebinding of `@ConfigurationProperties` (see below).
|
||||
|
||||
## ConfigurationPropertiesRebinder
|
||||
|
||||
Spring beans that are `@ConfigurationProperties` can be rebound if the `Environment` changes. Example:
|
||||
|
||||
```
|
||||
@ConfigurationProperties
|
||||
protected static class ServiceProperties {
|
||||
private String message;
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
then inject `ServiceProperties` into an application component using `@Autowired` and use it to govern the behaviour.
|
||||
When the `Environment` changes do this
|
||||
|
||||
```
|
||||
@Autowired
|
||||
private ConfigurationPropertiesRebinder rebinder;
|
||||
|
||||
...
|
||||
|
||||
rebinder.rebind();
|
||||
|
||||
```
|
||||
|
||||
The `ConfigurationPropertiesRebinder` is also a `@ManagedResource` so you can ping the `rebind()` operation remotely.
|
||||
|
||||
## RefreshScope
|
||||
|
||||
Spring beans in scope="refresh" are re-initialized on the next method call after a refresh. Example:
|
||||
|
||||
```
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(TestProperties.class)
|
||||
@EnableAutoConfiguration
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
then update the `TestProperties` (e.g. via JMX or something), and
|
||||
|
||||
```
|
||||
@Autowired
|
||||
private RefreshScope scope;
|
||||
|
||||
...
|
||||
|
||||
// ...and then refresh, so the bean is re-initialized:
|
||||
scope.refresh("service");
|
||||
```
|
||||
51
spring-platform-config-client/pom.xml
Normal file
51
spring-platform-config-client/pom.xml
Normal file
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>spring-platform-config-client</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>Spring Platform Config Client</name>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.platform</groupId>
|
||||
<artifactId>spring-platform-config</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
<url>http://spring.io</url>
|
||||
<description>
|
||||
<![CDATA[
|
||||
This project is a Spring configuration client.
|
||||
]]>
|
||||
</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>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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.platform.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.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.context.properties.ConfigurationBeanFactoryMetaData;
|
||||
import org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.platform.context.environment.EnvironmentManager;
|
||||
import org.springframework.platform.context.environment.EnvironmentManagerMvcEndpoint;
|
||||
import org.springframework.platform.context.properties.ConfigurationPropertiesRebinder;
|
||||
import org.springframework.platform.context.scope.refresh.RefreshScope;
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(RefreshScope.class)
|
||||
@AutoConfigureAfter(EndpointAutoConfiguration.class)
|
||||
public class RefreshAutoConfiguration {
|
||||
|
||||
@Autowired
|
||||
private ConfigurationPropertiesBindingPostProcessor binder;
|
||||
|
||||
@Autowired
|
||||
private ConfigurationBeanFactoryMetaData metaData;
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public static RefreshScope refreshScope() {
|
||||
return new RefreshScope();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public EnvironmentManager environmentManager(ConfigurableEnvironment environment) {
|
||||
return new EnvironmentManager(environment);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ConfigurationPropertiesRebinder configurationPropertiesRebinder() {
|
||||
ConfigurationPropertiesRebinder rebinder = new ConfigurationPropertiesRebinder(binder);
|
||||
rebinder.setBeanMetaDataStore(metaData);
|
||||
return rebinder;
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnWebApplication
|
||||
@ConditionalOnClass(EnvironmentEndpoint.class)
|
||||
@ConditionalOnExpression("${endpoints.env.enabled:true}")
|
||||
@ConditionalOnBean(EnvironmentEndpoint.class)
|
||||
protected static class EnvironmentEndpointConfiguration {
|
||||
|
||||
@Bean
|
||||
public EnvironmentManagerMvcEndpoint environmentManagerEndpoint(EnvironmentEndpoint delegate, EnvironmentManager environment) {
|
||||
return new EnvironmentManagerMvcEndpoint(delegate, environment);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* 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.platform.bootstrap;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
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.context.event.ContextClosedEvent;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
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;
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
|
||||
/**
|
||||
* 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 int DEFAULT_ORDER = Ordered.HIGHEST_PRECEDENCE + 5;
|
||||
|
||||
private int order = DEFAULT_ORDER;
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
|
||||
Environment environment = event.getEnvironment();
|
||||
if (environment instanceof ConfigurableEnvironment) {
|
||||
ConfigurableEnvironment configurable = (ConfigurableEnvironment) environment;
|
||||
// don't listen to events in a bootstrap context
|
||||
if (configurable.getPropertySources().contains("bootstrap")) {
|
||||
return;
|
||||
}
|
||||
ConfigurableApplicationContext context = bootstrapServiceContext(
|
||||
configurable, event.getSpringApplication());
|
||||
apply(context, event.getSpringApplication(), configurable);
|
||||
}
|
||||
}
|
||||
|
||||
private ConfigurableApplicationContext bootstrapServiceContext(
|
||||
ConfigurableEnvironment environment, final SpringApplication application) {
|
||||
StandardEnvironment bootstrapEnvironment = new StandardEnvironment();
|
||||
MutablePropertySources bootstrapProperties = bootstrapEnvironment.getPropertySources();
|
||||
for (PropertySource<?> source : bootstrapProperties) {
|
||||
bootstrapProperties.remove(source.getName());
|
||||
}
|
||||
for (PropertySource<?> source : environment.getPropertySources()) {
|
||||
bootstrapProperties.addLast(source);
|
||||
}
|
||||
bootstrapProperties.addFirst(new MapPropertySource("bootstrap",
|
||||
Collections.<String, Object> singletonMap("spring.config.name",
|
||||
"bootstrap")));
|
||||
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().showBanner(
|
||||
false).environment(bootstrapEnvironment).web(false).properties(
|
||||
"spring.application.name:bootstrap");
|
||||
builder.sources(names.toArray());
|
||||
final ConfigurableApplicationContext context = builder.run();
|
||||
// Shutdown the bootstrap context when the app closes
|
||||
// TODO: maybe make the bootstrap context a parent of the app context
|
||||
application.addListeners(new ApplicationListener<ContextClosedEvent>() {
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(ContextClosedEvent event) {
|
||||
context.close();
|
||||
}
|
||||
});
|
||||
return 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[0]));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.platform.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;
|
||||
|
||||
|
||||
@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 {};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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.platform.bootstrap.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
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.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
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.PropertySource;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties
|
||||
public class ConfigServiceBootstrapConfiguration implements
|
||||
ApplicationContextInitializer<ConfigurableApplicationContext> {
|
||||
|
||||
private static Log logger = LogFactory.getLog(ConfigServiceBootstrapConfiguration.class);
|
||||
|
||||
@Autowired(required = false)
|
||||
private List<PropertySourceLocator> propertySourceLocators = new ArrayList<PropertySourceLocator>();
|
||||
|
||||
@Override
|
||||
public void initialize(ConfigurableApplicationContext applicationContext) {
|
||||
CompositePropertySource composite = new CompositePropertySource("bootstrap");
|
||||
AnnotationAwareOrderComparator.sort(propertySourceLocators);
|
||||
boolean empty = true;
|
||||
for (PropertySourceLocator locator : propertySourceLocators) {
|
||||
PropertySource<?> source = null;
|
||||
try {
|
||||
source = locator.locate();
|
||||
} catch (Exception e) {
|
||||
logger.error("Could not locate PropertySource: " + e.getMessage());
|
||||
}
|
||||
if (source == null) {
|
||||
continue;
|
||||
}
|
||||
composite.addPropertySource(source);
|
||||
empty = false;
|
||||
}
|
||||
if (!empty) {
|
||||
applicationContext.getEnvironment().getPropertySources().addFirst(composite);
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConfigurationProperties("spring.platform.config")
|
||||
public ConfigServicePropertySourceLocator configServicePropertySource() {
|
||||
return new ConfigServicePropertySourceLocator();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* 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.platform.bootstrap.config;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.core.env.CompositePropertySource;
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class ConfigServicePropertySourceLocator implements PropertySourceLocator {
|
||||
|
||||
private String env = "development";
|
||||
|
||||
@Value("${spring.application.name:'application'}")
|
||||
private String name;
|
||||
|
||||
private String label = "master";
|
||||
|
||||
private String url = "http://localhost:8888";
|
||||
|
||||
private RestTemplate restTemplate = new RestTemplate();
|
||||
|
||||
@Override
|
||||
public PropertySource<?> locate() {
|
||||
CompositePropertySource composite = new CompositePropertySource("configService");
|
||||
List<SerializableMapPropertySource> result = restTemplate.exchange(
|
||||
url + "/{name}/{env}/{label}", HttpMethod.GET,
|
||||
new HttpEntity<Void>((Void) null),
|
||||
new ParameterizedTypeReference<List<SerializableMapPropertySource>>() {
|
||||
}, name, env, label).getBody();
|
||||
for (SerializableMapPropertySource source : result) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> map = (Map<String, Object>) source.getSource();
|
||||
composite.addPropertySource(new MapPropertySource(source.getName(), map));
|
||||
}
|
||||
return composite;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getEnv() {
|
||||
return env;
|
||||
}
|
||||
|
||||
public void setEnv(String env) {
|
||||
this.env = env;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public void setLabel(String label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
protected static class SerializableMapPropertySource {
|
||||
|
||||
private String name;
|
||||
|
||||
private Map<?, ?> source;
|
||||
|
||||
@JsonCreator
|
||||
public SerializableMapPropertySource(@JsonProperty("name") String name,
|
||||
@JsonProperty("source") Map<?, ?> source) {
|
||||
this.name = name;
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Map<?, ?> getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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.platform.bootstrap.config;
|
||||
|
||||
import org.springframework.core.env.PropertySource;
|
||||
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public interface PropertySourceLocator {
|
||||
|
||||
PropertySource<?> locate();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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.platform.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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* 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.platform.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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.platform.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;
|
||||
|
||||
@Target({ElementType.TYPE, ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Scope("refresh")
|
||||
@Documented
|
||||
public @interface RefreshScope {
|
||||
/**
|
||||
* @see Scope#proxyMode()
|
||||
*/
|
||||
ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.platform.context.environment;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* 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.platform.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.MapPropertySource;
|
||||
import org.springframework.jmx.export.annotation.ManagedOperation;
|
||||
import org.springframework.jmx.export.annotation.ManagedResource;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
|
||||
this.publisher = publisher;
|
||||
}
|
||||
|
||||
@ManagedOperation
|
||||
public void reset() {
|
||||
if (!map.isEmpty()) {
|
||||
Set<String> keys = map.keySet();
|
||||
map.clear();
|
||||
publish(new EnvironmentChangeEvent(keys));
|
||||
}
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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.platform.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;
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* 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.platform.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.context.ApplicationListener;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.jmx.export.annotation.ManagedAttribute;
|
||||
import org.springframework.jmx.export.annotation.ManagedOperation;
|
||||
import org.springframework.jmx.export.annotation.ManagedResource;
|
||||
import org.springframework.platform.context.environment.EnvironmentChangeEvent;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@Component
|
||||
@ManagedResource
|
||||
public class ConfigurationPropertiesRebinder implements BeanPostProcessor, ApplicationListener<EnvironmentChangeEvent> {
|
||||
|
||||
private ConfigurationBeanFactoryMetaData metaData;
|
||||
|
||||
private ConfigurationPropertiesBindingPostProcessor binder;
|
||||
|
||||
public ConfigurationPropertiesRebinder(
|
||||
ConfigurationPropertiesBindingPostProcessor binder) {
|
||||
this.binder = binder;
|
||||
}
|
||||
|
||||
private Map<String, Object> beans = new HashMap<String, Object>();
|
||||
|
||||
/**
|
||||
* @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);
|
||||
}
|
||||
|
||||
@ManagedAttribute
|
||||
public Set<String> getBeanNames() {
|
||||
return new HashSet<String>(beans.keySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(EnvironmentChangeEvent event) {
|
||||
rebind();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,490 @@
|
||||
/*
|
||||
* 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.platform.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.aop.scope.ScopedProxyUtils;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.ObjectFactory;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
import org.springframework.beans.factory.config.BeanDefinitionVisitor;
|
||||
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.BeanDefinitionReaderUtils;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.context.expression.BeanFactoryAccessor;
|
||||
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.platform.context.config.BeanLifecycleDecorator;
|
||||
import org.springframework.platform.context.config.BeanLifecycleDecorator.Context;
|
||||
import org.springframework.platform.context.config.StandardBeanLifecycleDecorator;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.StringValueResolver;
|
||||
|
||||
/**
|
||||
* <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);
|
||||
|
||||
private BeanLifecycleWrapperCache cache = new BeanLifecycleWrapperCache(
|
||||
new StandardScopeCache());
|
||||
|
||||
private String name = "generic";
|
||||
|
||||
private boolean proxyTargetClass = true;
|
||||
|
||||
private boolean autoProxy = 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flag to indicate that all scoped beans should automatically be proxied.
|
||||
* If true then scoped beans can be injected as dependencies of another
|
||||
* component and the concrete target will only be instantiated when it is
|
||||
* used. Proxying is a huge advantage if the context storage for the scope
|
||||
* cache is not available at configuration time (e.g. for thread-based, or
|
||||
* other transient scopes). If this flag is false you can expect maybe to
|
||||
* have to add extra meta-data to the bean definitions individually (e.g.
|
||||
* <aop:scoped-proxy/> for an XML configuration).
|
||||
*
|
||||
* @param autoProxy
|
||||
* the flag value to set, default is true
|
||||
*/
|
||||
public void setAutoProxy(boolean autoProxy) {
|
||||
this.autoProxy = autoProxy;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
this.beanFactory = beanFactory;
|
||||
|
||||
evaluationContext = new StandardEvaluationContext();
|
||||
evaluationContext.addPropertyAccessor(new BeanFactoryAccessor());
|
||||
|
||||
if (!autoProxy) {
|
||||
// No need to try and create proxies
|
||||
return;
|
||||
}
|
||||
|
||||
Assert.state(beanFactory instanceof BeanDefinitionRegistry,
|
||||
"BeanFactory was not a BeanDefinitionRegistry, so RefreshScope cannot be used.");
|
||||
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
|
||||
|
||||
for (String beanName : beanFactory.getBeanDefinitionNames()) {
|
||||
BeanDefinition definition = beanFactory.getBeanDefinition(beanName);
|
||||
// Replace this or any of its inner beans with scoped proxy if it
|
||||
// has this scope
|
||||
boolean scoped = name.equals(definition.getScope());
|
||||
Scopifier scopifier = new Scopifier(registry, name,
|
||||
proxyTargetClass, scoped);
|
||||
scopifier.visitBeanDefinition(definition);
|
||||
if (scoped) {
|
||||
createScopedProxy(beanName, definition, registry,
|
||||
proxyTargetClass);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 BeanDefinitionHolder createScopedProxy(String beanName,
|
||||
BeanDefinition definition, BeanDefinitionRegistry registry,
|
||||
boolean proxyTargetClass) {
|
||||
BeanDefinitionHolder proxyHolder = ScopedProxyUtils.createScopedProxy(
|
||||
new BeanDefinitionHolder(definition, beanName), registry,
|
||||
proxyTargetClass);
|
||||
registry.registerBeanDefinition(beanName,
|
||||
proxyHolder.getBeanDefinition());
|
||||
return proxyHolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class to scan a bean definition hierarchy and force the use of
|
||||
* auto-proxy for scoped beans.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
private static class Scopifier extends BeanDefinitionVisitor {
|
||||
|
||||
private final boolean proxyTargetClass;
|
||||
|
||||
private final BeanDefinitionRegistry registry;
|
||||
|
||||
private final String scope;
|
||||
|
||||
private final boolean scoped;
|
||||
|
||||
public Scopifier(BeanDefinitionRegistry registry, String scope,
|
||||
boolean proxyTargetClass, boolean scoped) {
|
||||
super(new StringValueResolver() {
|
||||
public String resolveStringValue(String value) {
|
||||
return value;
|
||||
}
|
||||
});
|
||||
this.registry = registry;
|
||||
this.proxyTargetClass = proxyTargetClass;
|
||||
this.scope = scope;
|
||||
this.scoped = scoped;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object resolveValue(Object value) {
|
||||
|
||||
BeanDefinition definition = null;
|
||||
String beanName = null;
|
||||
if (value instanceof BeanDefinition) {
|
||||
definition = (BeanDefinition) value;
|
||||
beanName = BeanDefinitionReaderUtils.generateBeanName(
|
||||
definition, registry);
|
||||
} else if (value instanceof BeanDefinitionHolder) {
|
||||
BeanDefinitionHolder holder = (BeanDefinitionHolder) value;
|
||||
definition = holder.getBeanDefinition();
|
||||
beanName = holder.getBeanName();
|
||||
}
|
||||
|
||||
if (definition != null) {
|
||||
boolean nestedScoped = scope.equals(definition.getScope());
|
||||
boolean scopeChangeRequiresProxy = !scoped && nestedScoped;
|
||||
if (scopeChangeRequiresProxy) {
|
||||
// Exit here so that nested inner bean definitions are not
|
||||
// analysed
|
||||
return createScopedProxy(beanName, definition, registry,
|
||||
proxyTargetClass);
|
||||
}
|
||||
}
|
||||
|
||||
// Nested inner bean definitions are recursively analysed here
|
||||
value = super.resolveValue(value);
|
||||
return value;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.platform.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);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.platform.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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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.platform.context.scope.refresh;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.springframework.jmx.export.annotation.ManagedOperation;
|
||||
import org.springframework.jmx.export.annotation.ManagedResource;
|
||||
import org.springframework.platform.context.scope.GenericScope;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A Scope implementation that allows for beans to be refreshed dynamically at runtime (see {@link #refresh(String)} and
|
||||
* {@link #refreshAll()}). If a bean is refreshed then the next time the bean is accessed (i.e. a method is executed) a
|
||||
* new instance is created. All lifecycle methods are applied to the bean instances, so any destruction callbacks that
|
||||
* were registered in the bean factory are called when it is refreshed, and then the initialization callbacks are
|
||||
* invoked as normal when the new instance is created. A new bean instance is created from the original bean definition,
|
||||
* so any externalized content (property placeholders or expressions in string literals) is re-evaluated when it is
|
||||
* created.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Note that all beans in this scope are <em>only</em> initialized when first accessed, so the scope forces lazy
|
||||
* initialization semantics. The implementation involves creating a proxy for every bean in the scope, so there is a
|
||||
* flag {@link #setProxyTargetClass(boolean) proxyTargetClass} which controls the proxy creation, defaulting to JDK
|
||||
* dynamic proxies and therefore only exposing the interfaces implemented by a bean. If callers need access to other
|
||||
* methods then the flag needs to be set (and CGLib present on the classpath). Because this scope automatically proxies
|
||||
* all its beans, there is no need to add <code><aop:auto-proxy/></code> to any bean definitions.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* The scoped proxy approach adopted here has a side benefit that bean instances are automatically {@link Serializable},
|
||||
* and can be sent across the wire as long as the receiver has an identical application context on the other side. To
|
||||
* ensure that the two contexts agree that they are identical they have to have the same serialization id. One will be
|
||||
* generated automatically by default from the bean names, so two contexts with the same bean names are by default able
|
||||
* to exchange beans by name. If you need to override the default id then provide an explicit {@link #setId(String) id}
|
||||
* when the Scope is declared.
|
||||
* </p>
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
* @since 3.1
|
||||
*
|
||||
*/
|
||||
@ManagedResource
|
||||
public class RefreshScope extends GenericScope {
|
||||
|
||||
/**
|
||||
* 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("scopedTarget.")) {
|
||||
// User wants to refresh the bean with this name but that isn't the one in the cache...
|
||||
name = "scopedTarget." + name;
|
||||
}
|
||||
// Ensure lifecycle is finished if bean was disposable
|
||||
super.destroy(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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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.platform.context.scope.thread;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import org.springframework.platform.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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.platform.context.scope.thread;
|
||||
|
||||
import org.springframework.platform.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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
# Auto Configure
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.springframework.platform.autoconfigure.RefreshAutoConfiguration
|
||||
|
||||
# Application Listeners
|
||||
org.springframework.context.ApplicationListener=\
|
||||
org.springframework.platform.bootstrap.BootstrapApplicationListener
|
||||
|
||||
# Bootstrap components
|
||||
org.springframework.platform.bootstrap.BootstrapConfiguration=\
|
||||
org.springframework.platform.bootstrap.config.ConfigServiceBootstrapConfiguration,\
|
||||
org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* 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.platform.bootstrap;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
import org.springframework.platform.bootstrap.config.PropertySourceLocator;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class BootstrapConfigurationTests {
|
||||
|
||||
private ConfigurableApplicationContext context;
|
||||
|
||||
@After
|
||||
public void close() {
|
||||
if (context != null) {
|
||||
context.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void picksUpAdditionalPropertySource() {
|
||||
context = new SpringApplicationBuilder().web(false).sources(
|
||||
BareConfiguration.class).run();
|
||||
assertEquals("bar", context.getEnvironment().getProperty("bootstrap.foo"));
|
||||
assertTrue(context.getEnvironment().getPropertySources().contains("bootstrap"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void environmentEnrichedOnce() {
|
||||
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());
|
||||
assertTrue(context.getEnvironment().getPropertySources().contains("bootstrap"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void environmentEnrichedInParent() {
|
||||
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"));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
protected static class BareConfiguration {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
protected static class PropertySourceConfiguration implements PropertySourceLocator {
|
||||
|
||||
@Override
|
||||
public PropertySource<?> locate() {
|
||||
return new MapPropertySource("testBootstrap",
|
||||
Collections.<String, Object> singletonMap("bootstrap.foo", "bar"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* 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.platform.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.platform.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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* 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.platform.context.properties;
|
||||
|
||||
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.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.platform.autoconfigure.RefreshAutoConfiguration;
|
||||
import org.springframework.platform.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());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DirtiesContext
|
||||
public void testRefresh() throws Exception {
|
||||
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());
|
||||
}
|
||||
|
||||
@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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
/*
|
||||
* 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.platform.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.platform.autoconfigure.RefreshAutoConfiguration;
|
||||
import org.springframework.platform.context.config.annotation.RefreshScope;
|
||||
import org.springframework.platform.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.platform.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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
* 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.platform.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.platform.autoconfigure.RefreshAutoConfiguration;
|
||||
import org.springframework.platform.context.config.annotation.RefreshScope;
|
||||
import org.springframework.platform.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.platform.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 {
|
||||
latch.countDown();
|
||||
return service.getMessage();
|
||||
} finally {
|
||||
logger.debug("Background done.");
|
||||
}
|
||||
}
|
||||
});
|
||||
assertTrue(latch.await(500, 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
* 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.platform.context.scope.refresh;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
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.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.platform.autoconfigure.RefreshAutoConfiguration;
|
||||
import org.springframework.platform.context.config.annotation.RefreshScope;
|
||||
import org.springframework.platform.context.scope.refresh.RefreshScopeIntegrationTests.TestConfiguration;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
@SpringApplicationConfiguration(classes=TestConfiguration.class)
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
public class RefreshScopeIntegrationTests {
|
||||
|
||||
@Autowired
|
||||
private Service service;
|
||||
|
||||
@Autowired
|
||||
private TestProperties properties;
|
||||
|
||||
@Autowired
|
||||
private org.springframework.platform.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);
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
public static interface Service {
|
||||
|
||||
String getMessage();
|
||||
|
||||
}
|
||||
|
||||
public static class ExampleService implements Service, InitializingBean, DisposableBean {
|
||||
|
||||
private static Log logger = LogFactory.getLog(ExampleService.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(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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
message: Hello scope!
|
||||
delay: 0
|
||||
# debug: true
|
||||
@@ -0,0 +1 @@
|
||||
spring.main.sources: org.springframework.platform.bootstrap.BootstrapConfigurationTests.PropertySourceConfiguration
|
||||
50
spring-platform-config-server/pom.xml
Normal file
50
spring-platform-config-server/pom.xml
Normal file
@@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>spring-platform-config-server</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>spring-platform-config-server</name>
|
||||
<description>spring-platform-config-server</description>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.platform</groupId>
|
||||
<artifactId>spring-platform-config</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<start-class>org.springframework.platform.config.server.Application</start-class>
|
||||
<java.version>1.7</java.version>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,81 @@
|
||||
|
||||
package org.springframework.platform.config.server;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.context.support.StandardServletEnvironment;
|
||||
|
||||
@Configuration
|
||||
@ComponentScan
|
||||
@EnableAutoConfiguration
|
||||
@RestController
|
||||
public class Application {
|
||||
|
||||
@Autowired
|
||||
private ConfigurableEnvironment environment;
|
||||
|
||||
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));
|
||||
|
||||
@RequestMapping("/{name}/{env}")
|
||||
public List<SerializableMapPropertySource> master(@PathVariable String env) {
|
||||
return properties(env, "master");
|
||||
}
|
||||
|
||||
@RequestMapping("/{name}/{env}/{label}")
|
||||
public List<SerializableMapPropertySource> properties(@PathVariable String env, @PathVariable String label) {
|
||||
List<SerializableMapPropertySource> result = new ArrayList<Application.SerializableMapPropertySource>();
|
||||
for (PropertySource<?> source : environment.getPropertySources()) {
|
||||
String name = source.getName();
|
||||
if (!standardSources .contains(name) && source instanceof MapPropertySource) {
|
||||
result.add(new SerializableMapPropertySource(name, (Map<?,?>)source.getSource()));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Application.class, args);
|
||||
}
|
||||
|
||||
protected static class SerializableMapPropertySource {
|
||||
|
||||
private String name;
|
||||
private Map<?, ?> source;
|
||||
|
||||
public SerializableMapPropertySource(String name, Map<?,?> source) {
|
||||
this.name = name;
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Map<?, ?> getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
info:
|
||||
component: Config Server
|
||||
spring:
|
||||
application:
|
||||
name: configserver
|
||||
server:
|
||||
port: 8888
|
||||
@@ -0,0 +1,18 @@
|
||||
package org.springframework.platform.config.server;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringApplicationConfiguration(classes = Application.class)
|
||||
@WebAppConfiguration
|
||||
public class ApplicationTests {
|
||||
|
||||
@Test
|
||||
public void contextLoads() {
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user