Add extra decryption step before propery source locators
See also the integration tests in spring-cloud-samples/tests Fixes gh-58
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.autoconfigure;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.springframework.beans.factory.SmartInitializingSingleton;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.SearchStrategy;
|
||||
import org.springframework.boot.context.properties.ConfigurationBeanFactoryMetaData;
|
||||
import org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor;
|
||||
import org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessorRegistrar;
|
||||
import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
|
||||
import org.springframework.cloud.context.properties.ConfigurationPropertiesBeans;
|
||||
import org.springframework.cloud.context.properties.ConfigurationPropertiesRebinder;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnBean(ConfigurationPropertiesBindingPostProcessor.class)
|
||||
public class ConfigurationPropertiesRebinderAutoConfiguration
|
||||
implements ApplicationContextAware, SmartInitializingSingleton {
|
||||
|
||||
private ApplicationContext context;
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) {
|
||||
this.context = applicationContext;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(search = SearchStrategy.CURRENT)
|
||||
public ConfigurationPropertiesBeans configurationPropertiesBeans() {
|
||||
// Since this is a BeanPostProcessor we have to be super careful not to
|
||||
// cause a cascade of bean instantiation. Knowing the *name* of the beans we
|
||||
// need is super optimal, but a little brittle (unfortunately we have no
|
||||
// choice).
|
||||
ConfigurationBeanFactoryMetaData metaData = this.context.getBean(
|
||||
ConfigurationPropertiesBindingPostProcessorRegistrar.BINDER_BEAN_NAME
|
||||
+ ".store",
|
||||
ConfigurationBeanFactoryMetaData.class);
|
||||
ConfigurationPropertiesBeans beans = new ConfigurationPropertiesBeans();
|
||||
beans.setBeanMetaDataStore(metaData);
|
||||
return beans;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(search = SearchStrategy.CURRENT)
|
||||
public ConfigurationPropertiesRebinder configurationPropertiesRebinder(
|
||||
ConfigurationPropertiesBeans beans,
|
||||
ConfigurationPropertiesBindingPostProcessor binder) {
|
||||
ConfigurationPropertiesRebinder rebinder = new ConfigurationPropertiesRebinder(
|
||||
binder, beans);
|
||||
return rebinder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterSingletonsInstantiated() {
|
||||
// After all beans are initialized send a pre-emptive EnvironmentChangeEvent
|
||||
// so that anything that needs to rebind gets a chance now (especially for
|
||||
// beans in the parent context)
|
||||
this.context.publishEvent(
|
||||
new EnvironmentChangeEvent(Collections.<String> emptySet()));
|
||||
}
|
||||
}
|
||||
@@ -17,12 +17,10 @@
|
||||
|
||||
package org.springframework.cloud.autoconfigure;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.SmartInitializingSingleton;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.boot.actuate.autoconfigure.EndpointAutoConfiguration;
|
||||
@@ -34,23 +32,15 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.condition.SearchStrategy;
|
||||
import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.ConfigurationBeanFactoryMetaData;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor;
|
||||
import org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessorRegistrar;
|
||||
import org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration;
|
||||
import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
|
||||
import org.springframework.cloud.context.environment.EnvironmentManager;
|
||||
import org.springframework.cloud.context.properties.ConfigurationPropertiesBeans;
|
||||
import org.springframework.cloud.context.properties.ConfigurationPropertiesRebinder;
|
||||
import org.springframework.cloud.context.restart.RestartEndpoint;
|
||||
import org.springframework.cloud.context.scope.refresh.RefreshScope;
|
||||
import org.springframework.cloud.endpoint.RefreshEndpoint;
|
||||
import org.springframework.cloud.logging.LoggingRebinder;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -138,53 +128,6 @@ public class RefreshAutoConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnBean(ConfigurationPropertiesBindingPostProcessor.class)
|
||||
protected static class ConfigurationPropertiesRebinderConfiguration implements
|
||||
ApplicationContextAware, SmartInitializingSingleton {
|
||||
|
||||
private ApplicationContext context;
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) {
|
||||
this.context = applicationContext;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(search = SearchStrategy.CURRENT)
|
||||
public ConfigurationPropertiesBeans configurationPropertiesBeans() {
|
||||
// Since this is a BeanPostProcessor we have to be super careful not to
|
||||
// cause a cascade of bean instantiation. Knowing the *name* of the beans we
|
||||
// need is super optimal, but a little brittle (unfortunately we have no
|
||||
// choice).
|
||||
ConfigurationBeanFactoryMetaData metaData = this.context.getBean(
|
||||
ConfigurationPropertiesBindingPostProcessorRegistrar.BINDER_BEAN_NAME
|
||||
+ ".store", ConfigurationBeanFactoryMetaData.class);
|
||||
ConfigurationPropertiesBeans beans = new ConfigurationPropertiesBeans();
|
||||
beans.setBeanMetaDataStore(metaData);
|
||||
return beans;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(search = SearchStrategy.CURRENT)
|
||||
public ConfigurationPropertiesRebinder configurationPropertiesRebinder(
|
||||
ConfigurationPropertiesBeans beans,
|
||||
ConfigurationPropertiesBindingPostProcessor binder) {
|
||||
ConfigurationPropertiesRebinder rebinder = new ConfigurationPropertiesRebinder(
|
||||
binder, beans);
|
||||
return rebinder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterSingletonsInstantiated() {
|
||||
// After all beans are initialized send a pre-emptive EnvironmentChangeEvent
|
||||
// so that anything that needs to rebind gets a chance now (especially for
|
||||
// beans in the parent context)
|
||||
this.context.publishEvent(new EnvironmentChangeEvent(Collections
|
||||
.<String> emptySet()));
|
||||
}
|
||||
}
|
||||
|
||||
@ConditionalOnClass(Endpoint.class)
|
||||
protected static class RefreshEndpointsConfiguration {
|
||||
|
||||
|
||||
@@ -23,15 +23,18 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.boot.Banner.Mode;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.builder.ParentContextApplicationContextInitializer;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
|
||||
import org.springframework.cloud.bootstrap.encrypt.EnvironmentDecryptApplicationInitializer;
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
import org.springframework.core.env.MutablePropertySources;
|
||||
@@ -52,8 +55,8 @@ import org.springframework.util.StringUtils;
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class BootstrapApplicationListener implements
|
||||
ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered {
|
||||
public class BootstrapApplicationListener
|
||||
implements ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered {
|
||||
|
||||
public static final String BOOTSTRAP_PROPERTY_SOURCE_NAME = "bootstrap";
|
||||
|
||||
@@ -94,8 +97,8 @@ public class BootstrapApplicationListener implements
|
||||
if (StringUtils.hasText(configLocation)) {
|
||||
bootstrapMap.put("spring.config.location", configLocation);
|
||||
}
|
||||
bootstrapProperties.addFirst(new MapPropertySource(
|
||||
BOOTSTRAP_PROPERTY_SOURCE_NAME, bootstrapMap));
|
||||
bootstrapProperties.addFirst(
|
||||
new MapPropertySource(BOOTSTRAP_PROPERTY_SOURCE_NAME, bootstrapMap));
|
||||
bootstrapProperties.addFirst(new MapPropertySource("bootstrapInProgress",
|
||||
Collections.<String, Object> emptyMap()));
|
||||
for (PropertySource<?> source : environment.getPropertySources()) {
|
||||
@@ -103,11 +106,11 @@ public class BootstrapApplicationListener implements
|
||||
}
|
||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||
// Use names and ensure unique to protect against duplicates
|
||||
List<String> names = SpringFactoriesLoader.loadFactoryNames(
|
||||
BootstrapConfiguration.class, classLoader);
|
||||
List<String> names = SpringFactoriesLoader
|
||||
.loadFactoryNames(BootstrapConfiguration.class, classLoader);
|
||||
// TODO: is it possible or sensible to share a ResourceLoader?
|
||||
SpringApplicationBuilder builder = new SpringApplicationBuilder()
|
||||
.profiles(environment.getActiveProfiles()).showBanner(false)
|
||||
.profiles(environment.getActiveProfiles()).bannerMode(Mode.OFF)
|
||||
.environment(bootstrapEnvironment)
|
||||
.properties("spring.application.name:" + configName).web(false);
|
||||
List<Class<?>> sources = new ArrayList<>();
|
||||
@@ -133,7 +136,8 @@ public class BootstrapApplicationListener implements
|
||||
private void addAncestorInitializer(SpringApplication application,
|
||||
ConfigurableApplicationContext context) {
|
||||
boolean installed = false;
|
||||
for (ApplicationContextInitializer<?> initializer : application.getInitializers()) {
|
||||
for (ApplicationContextInitializer<?> initializer : application
|
||||
.getInitializers()) {
|
||||
if (initializer instanceof AncestorInitializer) {
|
||||
installed = true;
|
||||
// New parent
|
||||
@@ -154,9 +158,27 @@ public class BootstrapApplicationListener implements
|
||||
ApplicationContextInitializer.class);
|
||||
application.addInitializers(initializers
|
||||
.toArray(new ApplicationContextInitializer[initializers.size()]));
|
||||
addBootstrapDecryptInitializer(application);
|
||||
}
|
||||
|
||||
private <T> List<T> getOrderedBeansOfType(ListableBeanFactory context, Class<T> type) {
|
||||
private void addBootstrapDecryptInitializer(SpringApplication application) {
|
||||
DelegatingEnvironmentDecryptApplicationInitializer decrypter = null;
|
||||
for (ApplicationContextInitializer<?> initializer : application
|
||||
.getInitializers()) {
|
||||
if (initializer instanceof EnvironmentDecryptApplicationInitializer) {
|
||||
@SuppressWarnings("unchecked")
|
||||
ApplicationContextInitializer<ConfigurableApplicationContext> delegate = (ApplicationContextInitializer<ConfigurableApplicationContext>) initializer;
|
||||
decrypter = new DelegatingEnvironmentDecryptApplicationInitializer(
|
||||
delegate);
|
||||
}
|
||||
}
|
||||
if (decrypter != null) {
|
||||
application.addInitializers(decrypter);
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
@@ -191,17 +213,16 @@ public class BootstrapApplicationListener implements
|
||||
public int getOrder() {
|
||||
// Need to run not too late (so not unordered), so that, for instance, the
|
||||
// ContextIdApplicationContextInitializer runs later and picks up the merged
|
||||
// Environment. Also not too early so that other initializers can pick up the
|
||||
// parent (especially the Environment).
|
||||
return Ordered.HIGHEST_PRECEDENCE + 10;
|
||||
// Environment. Also needs to be quite early so that other initializers can
|
||||
// pick up the parent (especially the Environment).
|
||||
return Ordered.HIGHEST_PRECEDENCE + 5;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(ConfigurableApplicationContext context) {
|
||||
preemptMerge(
|
||||
context.getEnvironment().getPropertySources(),
|
||||
new MapPropertySource(BOOTSTRAP_PROPERTY_SOURCE_NAME, Collections
|
||||
.<String, Object> emptyMap()));
|
||||
preemptMerge(context.getEnvironment().getPropertySources(),
|
||||
new MapPropertySource(BOOTSTRAP_PROPERTY_SOURCE_NAME,
|
||||
Collections.<String, Object> emptyMap()));
|
||||
while (context.getParent() != null && context.getParent() != context) {
|
||||
context = (ConfigurableApplicationContext) context.getParent();
|
||||
}
|
||||
@@ -217,4 +238,26 @@ public class BootstrapApplicationListener implements
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A special initializer designed to run before the property source bootstrap and
|
||||
* decrypt any properties needed there (e.g. URL of config server).
|
||||
*/
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE + 9)
|
||||
private static class DelegatingEnvironmentDecryptApplicationInitializer
|
||||
implements ApplicationContextInitializer<ConfigurableApplicationContext> {
|
||||
|
||||
private ApplicationContextInitializer<ConfigurableApplicationContext> delegate;
|
||||
|
||||
public DelegatingEnvironmentDecryptApplicationInitializer(
|
||||
ApplicationContextInitializer<ConfigurableApplicationContext> delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(ConfigurableApplicationContext applicationContext) {
|
||||
this.delegate.initialize(applicationContext);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.bootstrap.config;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.SearchStrategy;
|
||||
import org.springframework.boot.context.properties.ConfigurationBeanFactoryMetaData;
|
||||
import org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessorRegistrar;
|
||||
import org.springframework.cloud.context.properties.ConfigurationPropertiesBeans;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@Configuration
|
||||
public class ConfigurationPropertiesBootstrapConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(search = SearchStrategy.CURRENT)
|
||||
public ConfigurationPropertiesBeans configurationPropertiesBeans(
|
||||
ApplicationContext context) {
|
||||
// Since this is a BeanPostProcessor we have to be super careful not to
|
||||
// cause a cascade of bean instantiation. Knowing the *name* of the beans we
|
||||
// need is super optimal, but a little brittle (unfortunately we have no
|
||||
// choice).
|
||||
ConfigurationBeanFactoryMetaData metaData = context.getBean(
|
||||
ConfigurationPropertiesBindingPostProcessorRegistrar.BINDER_BEAN_NAME
|
||||
+ ".store", ConfigurationBeanFactoryMetaData.class);
|
||||
ConfigurationPropertiesBeans rebinder = new ConfigurationPropertiesBeans();
|
||||
rebinder.setBeanMetaDataStore(metaData);
|
||||
return rebinder;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -78,6 +78,10 @@ public class EnvironmentDecryptApplicationInitializer implements
|
||||
return this.order;
|
||||
}
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(ConfigurableApplicationContext applicationContext) {
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# AutoConfiguration
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration,\
|
||||
org.springframework.cloud.autoconfigure.RefreshAutoConfiguration,\
|
||||
org.springframework.cloud.autoconfigure.LifecycleMvcEndpointAutoConfiguration
|
||||
|
||||
@@ -10,7 +11,7 @@ org.springframework.cloud.context.restart.RestartListener
|
||||
|
||||
# Bootstrap components
|
||||
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
|
||||
org.springframework.cloud.bootstrap.config.ConfigurationPropertiesBootstrapConfiguration,\
|
||||
org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration,\
|
||||
org.springframework.cloud.bootstrap.encrypt.EncryptionBootstrapConfiguration,\
|
||||
org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration
|
||||
@@ -27,6 +27,7 @@ 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.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration;
|
||||
import org.springframework.cloud.autoconfigure.RefreshAutoConfiguration;
|
||||
import org.springframework.cloud.context.properties.ConfigurationPropertiesRebinderIntegrationTests.TestConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -104,7 +105,7 @@ public class ConfigurationPropertiesRebinderIntegrationTests {
|
||||
protected static class RefreshConfiguration extends RefreshAutoConfiguration {
|
||||
@Configuration
|
||||
protected static class RebinderConfiguration extends
|
||||
ConfigurationPropertiesRebinderConfiguration {
|
||||
ConfigurationPropertiesRebinderAutoConfiguration {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
|
||||
import org.springframework.boot.test.EnvironmentTestUtils;
|
||||
import org.springframework.boot.test.IntegrationTest;
|
||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration;
|
||||
import org.springframework.cloud.autoconfigure.RefreshAutoConfiguration;
|
||||
import org.springframework.cloud.context.properties.ConfigurationPropertiesRebinderListIntegrationTests.TestConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -105,7 +106,7 @@ public class ConfigurationPropertiesRebinderListIntegrationTests {
|
||||
// Hack out a protected inner class for testing
|
||||
protected static class RefreshConfiguration extends RefreshAutoConfiguration {
|
||||
@Configuration
|
||||
protected static class RebinderConfiguration extends ConfigurationPropertiesRebinderConfiguration {
|
||||
protected static class RebinderConfiguration extends ConfigurationPropertiesRebinderAutoConfiguration {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user