Refactor BootstrapRegistry support
Refactor `BootstrapRegistry` support following initial prototype work with the Spring Cloud team. This update splits the `BootstrapRegistry` API into `BootstrapRegistry`, `BootstrapContext` and `ConfigurableBootstrapContext` interfaces and moves it to the same package as `SpringApplication`. A new `Bootstrapper` interface has been introduced that can be added to the `SpringApplication` to customize the `BootstrapRegistry` before it's used. Closes gh-23326
This commit is contained in:
parent
27095d9043
commit
1ae1436211
|
|
@ -37,6 +37,7 @@ import org.junit.jupiter.api.io.TempDir;
|
|||
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.boot.DefaultBootstrapContext;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration;
|
||||
|
|
@ -78,8 +79,8 @@ class LiquibaseAutoConfigurationTests {
|
|||
|
||||
@BeforeEach
|
||||
void init() {
|
||||
new LiquibaseServiceLocatorApplicationListener()
|
||||
.onApplicationEvent(new ApplicationStartingEvent(new SpringApplication(Object.class), new String[0]));
|
||||
new LiquibaseServiceLocatorApplicationListener().onApplicationEvent(new ApplicationStartingEvent(
|
||||
new DefaultBootstrapContext(), new SpringApplication(Object.class), new String[0]));
|
||||
}
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
* Copyright 2012-2020 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.
|
||||
|
|
@ -23,6 +23,7 @@ import org.junit.jupiter.api.BeforeEach;
|
|||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
import org.springframework.boot.DefaultBootstrapContext;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.context.event.ApplicationFailedEvent;
|
||||
import org.springframework.boot.context.event.ApplicationPreparedEvent;
|
||||
|
|
@ -90,9 +91,10 @@ class RestartApplicationListenerTests {
|
|||
private void testInitialize(boolean failed) {
|
||||
Restarter.clearInstance();
|
||||
RestartApplicationListener listener = new RestartApplicationListener();
|
||||
DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
|
||||
SpringApplication application = new SpringApplication();
|
||||
ConfigurableApplicationContext context = mock(ConfigurableApplicationContext.class);
|
||||
listener.onApplicationEvent(new ApplicationStartingEvent(application, ARGS));
|
||||
listener.onApplicationEvent(new ApplicationStartingEvent(bootstrapContext, application, ARGS));
|
||||
assertThat(Restarter.getInstance()).isNotEqualTo(nullValue());
|
||||
assertThat(Restarter.getInstance().isFinished()).isFalse();
|
||||
listener.onApplicationEvent(new ApplicationPreparedEvent(application, ARGS, context));
|
||||
|
|
|
|||
|
|
@ -16,10 +16,10 @@
|
|||
|
||||
package org.springframework.boot.test.context;
|
||||
|
||||
import org.springframework.boot.DefaultBootstrapContext;
|
||||
import org.springframework.boot.DefaultPropertiesPropertySource;
|
||||
import org.springframework.boot.context.config.ConfigData;
|
||||
import org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor;
|
||||
import org.springframework.boot.env.DefaultBootstrapRegisty;
|
||||
import org.springframework.boot.env.RandomValuePropertySource;
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
|
@ -42,9 +42,9 @@ public class ConfigDataApplicationContextInitializer
|
|||
public void initialize(ConfigurableApplicationContext applicationContext) {
|
||||
ConfigurableEnvironment environment = applicationContext.getEnvironment();
|
||||
RandomValuePropertySource.addToEnvironment(environment);
|
||||
DefaultBootstrapRegisty bootstrapRegistry = new DefaultBootstrapRegisty();
|
||||
ConfigDataEnvironmentPostProcessor.applyTo(environment, applicationContext, bootstrapRegistry);
|
||||
bootstrapRegistry.applicationContextPrepared(applicationContext);
|
||||
DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
|
||||
ConfigDataEnvironmentPostProcessor.applyTo(environment, applicationContext, bootstrapContext);
|
||||
bootstrapContext.close(applicationContext);
|
||||
DefaultPropertiesPropertySource.moveToEnd(environment);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright 2012-2020 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
|
||||
*
|
||||
* https://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.boot;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
/**
|
||||
* A simple bootstrap context that is available during startup and {@link Environment}
|
||||
* post-processing up to the point that the {@link ApplicationContext} is prepared.
|
||||
* <p>
|
||||
* Provides lazy access to singletons that may be expensive to create, or need to be
|
||||
* shared before the {@link ApplicationContext} is available.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 2.4.0
|
||||
*/
|
||||
public interface BootstrapContext {
|
||||
|
||||
/**
|
||||
* Return an instance from the context, creating it if it hasn't been accessed
|
||||
* previously.
|
||||
* @param <T> the instance type
|
||||
* @param type the instance type
|
||||
* @return the instance managed by the context
|
||||
* @throws IllegalStateException if the type has not been registered
|
||||
*/
|
||||
<T> T get(Class<T> type) throws IllegalStateException;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright 2012-2020 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
|
||||
*
|
||||
* https://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.boot;
|
||||
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
/**
|
||||
* {@link ApplicationEvent} published by a {@link BootstrapContext} when it's closed.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 2.4.0
|
||||
* @see BootstrapRegistry#addCloseListener(org.springframework.context.ApplicationListener)
|
||||
*/
|
||||
public class BootstrapContextClosedEvent extends ApplicationEvent {
|
||||
|
||||
private final ConfigurableApplicationContext applicationContext;
|
||||
|
||||
BootstrapContextClosedEvent(BootstrapContext source, ConfigurableApplicationContext applicationContext) {
|
||||
super(source);
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link BootstrapContext} that was closed.
|
||||
* @return the bootstrap context
|
||||
*/
|
||||
public BootstrapContext getBootstrapContext() {
|
||||
return (BootstrapContext) this.source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the prepared application context.
|
||||
* @return the application context
|
||||
*/
|
||||
public ConfigurableApplicationContext getApplicationContext() {
|
||||
return this.applicationContext;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* Copyright 2012-2020 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
|
||||
*
|
||||
* https://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.boot;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import io.undertow.servlet.api.InstanceFactory;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
/**
|
||||
* A simple object registry that is available during startup and {@link Environment}
|
||||
* post-processing up to the point that the {@link ApplicationContext} is prepared.
|
||||
* <p>
|
||||
* Can be used to register instances that may be expensive to create, or need to be shared
|
||||
* before the {@link ApplicationContext} is available.
|
||||
* <p>
|
||||
* The registry uses {@link Class} as a key, meaning that only a single instance of a
|
||||
* given type can be stored.
|
||||
* <p>
|
||||
* The {@link #addCloseListener(ApplicationListener)} method can be used to add a listener
|
||||
* that can perform actions when {@link BootstrapContext} has been closed and the
|
||||
* {@link ApplicationContext} is fully prepared. For example, an instance may choose to
|
||||
* register itself as a regular Spring bean so that it is available for the application to
|
||||
* use.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 2.4.0
|
||||
* @see BootstrapContext
|
||||
* @see ConfigurableBootstrapContext
|
||||
*/
|
||||
public interface BootstrapRegistry {
|
||||
|
||||
/**
|
||||
* Register a specific type with the registry. If the specified type has already been
|
||||
* registered, but not get obtained, it will be replaced.
|
||||
* @param <T> the instance type
|
||||
* @param type the instance type
|
||||
* @param instanceSupplier the instance supplier
|
||||
*/
|
||||
<T> void register(Class<T> type, InstanceSupplier<T> instanceSupplier);
|
||||
|
||||
/**
|
||||
* Register a specific type with the registry if one is not already present.
|
||||
* @param <T> the instance type
|
||||
* @param type the instance type
|
||||
* @param instanceSupplier the instance supplier
|
||||
*/
|
||||
<T> void registerIfAbsent(Class<T> type, InstanceSupplier<T> instanceSupplier);
|
||||
|
||||
/**
|
||||
* Return if a registration exists for the given type.
|
||||
* @param <T> the instance type
|
||||
* @param type the instance type
|
||||
* @return {@code true} if the type has already been registered
|
||||
*/
|
||||
<T> boolean isRegistered(Class<T> type);
|
||||
|
||||
/**
|
||||
* Return any existing {@link InstanceFactory} for the given type.
|
||||
* @param <T> the instance type
|
||||
* @param type the instance type
|
||||
* @return the registered {@link InstanceSupplier} or {@code null}
|
||||
*/
|
||||
<T> InstanceSupplier<T> getRegisteredInstanceSupplier(Class<T> type);
|
||||
|
||||
/**
|
||||
* Add an {@link ApplicationListener} that will be called with a
|
||||
* {@link BootstrapContextClosedEvent} when the {@link BootstrapContext} is closed and
|
||||
* the {@link ApplicationContext} has been prepared.
|
||||
* @param listener the listener to add
|
||||
*/
|
||||
void addCloseListener(ApplicationListener<BootstrapContextClosedEvent> listener);
|
||||
|
||||
/**
|
||||
* Supplier used to provide the actual instance the first time it is accessed.
|
||||
*
|
||||
* @param <T> the instance type
|
||||
*/
|
||||
public interface InstanceSupplier<T> {
|
||||
|
||||
/**
|
||||
* Factory method used to create the instance when needed.
|
||||
* @param context the {@link BootstrapContext} which may be used to obtain other
|
||||
* bootstrap instances.
|
||||
* @return the instance
|
||||
*/
|
||||
T get(BootstrapContext context);
|
||||
|
||||
/**
|
||||
* Factory method that can be used to create a {@link InstanceFactory} for a given
|
||||
* instance.
|
||||
* @param <T> the instance type
|
||||
* @param instance the instance
|
||||
* @return a new {@link InstanceFactory}
|
||||
*/
|
||||
static <T> InstanceSupplier<T> of(T instance) {
|
||||
return (registry) -> instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method that can be used to create a {@link InstanceFactory} from a
|
||||
* {@link Supplier}.
|
||||
* @param <T> the instance type
|
||||
* @param supplier the supplier that will provide the instance
|
||||
* @return a new {@link InstanceFactory}
|
||||
*/
|
||||
static <T> InstanceSupplier<T> from(Supplier<T> supplier) {
|
||||
return (registry) -> (supplier != null) ? supplier.get() : null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright 2012-2020 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
|
||||
*
|
||||
* https://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.boot;
|
||||
|
||||
/**
|
||||
* Callback interface that can be used to initialize a {@link BootstrapRegistry} before it
|
||||
* is used.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 2.4.0
|
||||
* @see SpringApplication#addBootstrapper(Bootstrapper)
|
||||
* @see BootstrapRegistry
|
||||
*/
|
||||
public interface Bootstrapper {
|
||||
|
||||
/**
|
||||
* Initialize the given {@link BootstrapRegistry} with any required registrations.
|
||||
* @param registry the registry to initialize
|
||||
*/
|
||||
void intitialize(BootstrapRegistry registry);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright 2012-2020 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
|
||||
*
|
||||
* https://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.boot;
|
||||
|
||||
/**
|
||||
* A {@link BootstrapContext} that also provides configuration methods via the
|
||||
* {@link BootstrapRegistry} interface.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 2.4.0
|
||||
* @see BootstrapRegistry
|
||||
* @see BootstrapContext
|
||||
* @see DefaultBootstrapContext
|
||||
*/
|
||||
public interface ConfigurableBootstrapContext extends BootstrapRegistry, BootstrapContext {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* Copyright 2012-2020 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
|
||||
*
|
||||
* https://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.boot;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.event.ApplicationEventMulticaster;
|
||||
import org.springframework.context.event.SimpleApplicationEventMulticaster;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Default {@link ConfigurableBootstrapContext} implementation.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 2.4.0
|
||||
*/
|
||||
public class DefaultBootstrapContext implements ConfigurableBootstrapContext {
|
||||
|
||||
private final Map<Class<?>, InstanceSupplier<?>> instanceSuppliers = new HashMap<>();
|
||||
|
||||
private final Map<Class<?>, Object> instances = new HashMap<>();
|
||||
|
||||
private final ApplicationEventMulticaster events = new SimpleApplicationEventMulticaster();
|
||||
|
||||
@Override
|
||||
public <T> void register(Class<T> type, InstanceSupplier<T> instanceSupplier) {
|
||||
register(type, instanceSupplier, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void registerIfAbsent(Class<T> type, InstanceSupplier<T> instanceSupplier) {
|
||||
register(type, instanceSupplier, false);
|
||||
}
|
||||
|
||||
private <T> void register(Class<T> type, InstanceSupplier<T> instanceSupplier, boolean replaceExisting) {
|
||||
Assert.notNull(type, "Type must not be null");
|
||||
Assert.notNull(instanceSupplier, "InstanceSupplier must not be null");
|
||||
synchronized (this.instanceSuppliers) {
|
||||
boolean alreadyRegistered = this.instanceSuppliers.containsKey(type);
|
||||
if (replaceExisting || !alreadyRegistered) {
|
||||
Assert.state(!this.instances.containsKey(type), () -> type.getName() + " has already been created");
|
||||
this.instanceSuppliers.put(type, instanceSupplier);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> boolean isRegistered(Class<T> type) {
|
||||
synchronized (this.instanceSuppliers) {
|
||||
return this.instanceSuppliers.containsKey(type);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> InstanceSupplier<T> getRegisteredInstanceSupplier(Class<T> type) {
|
||||
synchronized (this.instanceSuppliers) {
|
||||
return (InstanceSupplier<T>) this.instanceSuppliers.get(type);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCloseListener(ApplicationListener<BootstrapContextClosedEvent> listener) {
|
||||
this.events.addApplicationListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T get(Class<T> type) throws IllegalStateException {
|
||||
synchronized (this.instanceSuppliers) {
|
||||
InstanceSupplier<?> instanceSupplier = this.instanceSuppliers.get(type);
|
||||
Assert.state(instanceSupplier != null, () -> type.getName() + " has not been registered");
|
||||
return (T) this.instances.computeIfAbsent(type, (key) -> instanceSupplier.get(this));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to be called when {@link BootstrapContext} is closed and the
|
||||
* {@link ApplicationContext} is prepared.
|
||||
* @param applicationContext the prepared context
|
||||
*/
|
||||
public void close(ConfigurableApplicationContext applicationContext) {
|
||||
this.events.multicastEvent(new BootstrapContextClosedEvent(this, applicationContext));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -236,6 +236,8 @@ public class SpringApplication {
|
|||
|
||||
private Map<String, Object> defaultProperties;
|
||||
|
||||
private List<Bootstrapper> bootstrappers;
|
||||
|
||||
private Set<String> additionalProfiles = Collections.emptySet();
|
||||
|
||||
private boolean allowBeanDefinitionOverriding;
|
||||
|
|
@ -278,6 +280,7 @@ public class SpringApplication {
|
|||
Assert.notNull(primarySources, "PrimarySources must not be null");
|
||||
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
|
||||
this.webApplicationType = WebApplicationType.deduceFromClasspath();
|
||||
this.bootstrappers = new ArrayList<>(getSpringFactoriesInstances(Bootstrapper.class));
|
||||
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
|
||||
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
|
||||
this.mainApplicationClass = deduceMainApplicationClass();
|
||||
|
|
@ -307,21 +310,22 @@ public class SpringApplication {
|
|||
public ConfigurableApplicationContext run(String... args) {
|
||||
StopWatch stopWatch = new StopWatch();
|
||||
stopWatch.start();
|
||||
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
|
||||
ConfigurableApplicationContext context = null;
|
||||
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
|
||||
configureHeadlessProperty();
|
||||
SpringApplicationRunListeners listeners = getRunListeners(args);
|
||||
listeners.starting(this.mainApplicationClass);
|
||||
listeners.starting(bootstrapContext, this.mainApplicationClass);
|
||||
try {
|
||||
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
|
||||
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
|
||||
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
|
||||
configureIgnoreBeanInfo(environment);
|
||||
Banner printedBanner = printBanner(environment);
|
||||
context = createApplicationContext();
|
||||
context.setApplicationStartup(this.applicationStartup);
|
||||
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
|
||||
new Class<?>[] { ConfigurableApplicationContext.class }, context);
|
||||
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
|
||||
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
|
||||
refreshContext(context);
|
||||
afterRefresh(context, applicationArguments);
|
||||
stopWatch.stop();
|
||||
|
|
@ -346,13 +350,19 @@ public class SpringApplication {
|
|||
return context;
|
||||
}
|
||||
|
||||
private DefaultBootstrapContext createBootstrapContext() {
|
||||
DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
|
||||
this.bootstrappers.forEach((initializer) -> initializer.intitialize(bootstrapContext));
|
||||
return bootstrapContext;
|
||||
}
|
||||
|
||||
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
|
||||
ApplicationArguments applicationArguments) {
|
||||
DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
|
||||
// Create and configure the environment
|
||||
ConfigurableEnvironment environment = getOrCreateEnvironment();
|
||||
configureEnvironment(environment, applicationArguments.getSourceArgs());
|
||||
ConfigurationPropertySources.attach(environment);
|
||||
listeners.environmentPrepared(environment);
|
||||
listeners.environmentPrepared(bootstrapContext, environment);
|
||||
DefaultPropertiesPropertySource.moveToEnd(environment);
|
||||
configureAdditionalProfiles(environment);
|
||||
bindToSpringApplication(environment);
|
||||
|
|
@ -375,12 +385,14 @@ public class SpringApplication {
|
|||
}
|
||||
}
|
||||
|
||||
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
|
||||
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
|
||||
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
|
||||
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
|
||||
ApplicationArguments applicationArguments, Banner printedBanner) {
|
||||
context.setEnvironment(environment);
|
||||
postProcessApplicationContext(context);
|
||||
applyInitializers(context);
|
||||
listeners.contextPrepared(context);
|
||||
bootstrapContext.close(context);
|
||||
if (this.logStartupInfo) {
|
||||
logStartupInfo(context.getParent() == null);
|
||||
logStartupProfileInfo(context);
|
||||
|
|
@ -1028,6 +1040,17 @@ public class SpringApplication {
|
|||
this.addConversionService = addConversionService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a {@link Bootstrapper} that can be used to initialize the
|
||||
* {@link BootstrapRegistry}.
|
||||
* @param bootstrapper the bootstraper
|
||||
* @since 2.4.0
|
||||
*/
|
||||
public void addBootstrapper(Bootstrapper bootstrapper) {
|
||||
Assert.notNull(bootstrapper, "Bootstrapper must not be null");
|
||||
this.bootstrappers.add(bootstrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set default environment properties which will be used in addition to those in the
|
||||
* existing {@link Environment}.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
* Copyright 2012-2020 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.
|
||||
|
|
@ -38,15 +38,40 @@ public interface SpringApplicationRunListener {
|
|||
/**
|
||||
* Called immediately when the run method has first started. Can be used for very
|
||||
* early initialization.
|
||||
* @param bootstrapContext the bootstrap context
|
||||
*/
|
||||
default void starting(ConfigurableBootstrapContext bootstrapContext) {
|
||||
starting();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called immediately when the run method has first started. Can be used for very
|
||||
* early initialization.
|
||||
* @deprecated since 2.4.0 in favor of {@link #starting(ConfigurableBootstrapContext)}
|
||||
*/
|
||||
@Deprecated
|
||||
default void starting() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called once the environment has been prepared, but before the
|
||||
* {@link ApplicationContext} has been created.
|
||||
* @param bootstrapContext the bootstrap context
|
||||
* @param environment the environment
|
||||
*/
|
||||
default void environmentPrepared(ConfigurableBootstrapContext bootstrapContext,
|
||||
ConfigurableEnvironment environment) {
|
||||
environmentPrepared(environment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called once the environment has been prepared, but before the
|
||||
* {@link ApplicationContext} has been created.
|
||||
* @param environment the environment
|
||||
* @deprecated since 2.4.0 in favor of
|
||||
* {@link #environmentPrepared(ConfigurableBootstrapContext, ConfigurableEnvironment)}
|
||||
*/
|
||||
@Deprecated
|
||||
default void environmentPrepared(ConfigurableEnvironment environment) {
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -49,17 +49,18 @@ class SpringApplicationRunListeners {
|
|||
this.applicationStartup = applicationStartup;
|
||||
}
|
||||
|
||||
void starting(Class<?> mainApplicationClass) {
|
||||
doWithListeners("spring.boot.application.starting", SpringApplicationRunListener::starting, (step) -> {
|
||||
if (mainApplicationClass != null) {
|
||||
step.tag("mainApplicationClass", mainApplicationClass.getName());
|
||||
}
|
||||
});
|
||||
void starting(ConfigurableBootstrapContext bootstrapContext, Class<?> mainApplicationClass) {
|
||||
doWithListeners("spring.boot.application.starting", (listener) -> listener.starting(bootstrapContext),
|
||||
(step) -> {
|
||||
if (mainApplicationClass != null) {
|
||||
step.tag("mainApplicationClass", mainApplicationClass.getName());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void environmentPrepared(ConfigurableEnvironment environment) {
|
||||
void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {
|
||||
doWithListeners("spring.boot.application.environment-prepared",
|
||||
(listener) -> listener.environmentPrepared(environment));
|
||||
(listener) -> listener.environmentPrepared(bootstrapContext, environment));
|
||||
}
|
||||
|
||||
void contextPrepared(ConfigurableApplicationContext context) {
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
import org.springframework.beans.factory.support.BeanNameGenerator;
|
||||
import org.springframework.boot.ApplicationContextFactory;
|
||||
import org.springframework.boot.Banner;
|
||||
import org.springframework.boot.BootstrapRegistry;
|
||||
import org.springframework.boot.Bootstrapper;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.WebApplicationType;
|
||||
import org.springframework.boot.convert.ApplicationConversionService;
|
||||
|
|
@ -397,6 +399,18 @@ public class SpringApplicationBuilder {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a {@link Bootstrapper} that can be used to initialize the
|
||||
* {@link BootstrapRegistry}.
|
||||
* @param bootstrapper the bootstraper
|
||||
* @return the current builder
|
||||
* @since 2.4.0
|
||||
*/
|
||||
public SpringApplicationBuilder addBootstrapper(Bootstrapper bootstrapper) {
|
||||
this.application.addBootstrapper(bootstrapper);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flag to control whether the application should be initialized lazily.
|
||||
* @param lazyInitialization the flag to set. Defaults to false.
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import java.util.Set;
|
|||
|
||||
import org.apache.commons.logging.Log;
|
||||
|
||||
import org.springframework.boot.ConfigurableBootstrapContext;
|
||||
import org.springframework.boot.DefaultPropertiesPropertySource;
|
||||
import org.springframework.boot.context.config.ConfigDataEnvironmentContributors.BinderOption;
|
||||
import org.springframework.boot.context.properties.bind.BindException;
|
||||
|
|
@ -33,7 +34,6 @@ import org.springframework.boot.context.properties.bind.Binder;
|
|||
import org.springframework.boot.context.properties.bind.PlaceholdersResolver;
|
||||
import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
|
||||
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
|
||||
import org.springframework.boot.env.BootstrapRegistry;
|
||||
import org.springframework.boot.logging.DeferredLogFactory;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
|
@ -97,7 +97,7 @@ class ConfigDataEnvironment {
|
|||
|
||||
private final Log logger;
|
||||
|
||||
private final BootstrapRegistry bootstrapRegistry;
|
||||
private final ConfigurableBootstrapContext bootstrapContext;
|
||||
|
||||
private final ConfigurableEnvironment environment;
|
||||
|
||||
|
|
@ -112,12 +112,12 @@ class ConfigDataEnvironment {
|
|||
/**
|
||||
* Create a new {@link ConfigDataEnvironment} instance.
|
||||
* @param logFactory the deferred log factory
|
||||
* @param bootstrapRegistry the bootstrap registry
|
||||
* @param bootstrapContext the bootstrap context
|
||||
* @param environment the Spring {@link Environment}.
|
||||
* @param resourceLoader {@link ResourceLoader} to load resource locations
|
||||
* @param additionalProfiles any additional profiles to activate
|
||||
*/
|
||||
ConfigDataEnvironment(DeferredLogFactory logFactory, BootstrapRegistry bootstrapRegistry,
|
||||
ConfigDataEnvironment(DeferredLogFactory logFactory, ConfigurableBootstrapContext bootstrapContext,
|
||||
ConfigurableEnvironment environment, ResourceLoader resourceLoader, Collection<String> additionalProfiles) {
|
||||
Binder binder = Binder.get(environment);
|
||||
UseLegacyConfigProcessingException.throwIfRequested(binder);
|
||||
|
|
@ -126,17 +126,20 @@ class ConfigDataEnvironment {
|
|||
.orElse(ConfigDataLocationNotFoundAction.FAIL);
|
||||
this.logFactory = logFactory;
|
||||
this.logger = logFactory.getLog(getClass());
|
||||
this.bootstrapRegistry = bootstrapRegistry;
|
||||
this.bootstrapContext = bootstrapContext;
|
||||
this.environment = environment;
|
||||
this.resolvers = createConfigDataLocationResolvers(logFactory, locationNotFoundAction, binder, resourceLoader);
|
||||
this.resolvers = createConfigDataLocationResolvers(logFactory, bootstrapContext, locationNotFoundAction, binder,
|
||||
resourceLoader);
|
||||
this.additionalProfiles = additionalProfiles;
|
||||
this.loaders = new ConfigDataLoaders(logFactory, locationNotFoundAction);
|
||||
this.loaders = new ConfigDataLoaders(logFactory, bootstrapContext, locationNotFoundAction);
|
||||
this.contributors = createContributors(binder);
|
||||
}
|
||||
|
||||
protected ConfigDataLocationResolvers createConfigDataLocationResolvers(DeferredLogFactory logFactory,
|
||||
ConfigDataLocationNotFoundAction locationNotFoundAction, Binder binder, ResourceLoader resourceLoader) {
|
||||
return new ConfigDataLocationResolvers(logFactory, locationNotFoundAction, binder, resourceLoader);
|
||||
ConfigurableBootstrapContext bootstrapContext, ConfigDataLocationNotFoundAction locationNotFoundAction,
|
||||
Binder binder, ResourceLoader resourceLoader) {
|
||||
return new ConfigDataLocationResolvers(logFactory, bootstrapContext, locationNotFoundAction, binder,
|
||||
resourceLoader);
|
||||
}
|
||||
|
||||
private ConfigDataEnvironmentContributors createContributors(Binder binder) {
|
||||
|
|
@ -159,7 +162,7 @@ class ConfigDataEnvironment {
|
|||
this.logger.trace("Creating wrapped config data contributor for default property source");
|
||||
contributors.add(ConfigDataEnvironmentContributor.ofExisting(defaultPropertySource));
|
||||
}
|
||||
return new ConfigDataEnvironmentContributors(this.logFactory, this.bootstrapRegistry, contributors);
|
||||
return new ConfigDataEnvironmentContributors(this.logFactory, this.bootstrapContext, contributors);
|
||||
}
|
||||
|
||||
ConfigDataEnvironmentContributors getContributors() {
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import java.util.stream.Stream;
|
|||
|
||||
import org.apache.commons.logging.Log;
|
||||
|
||||
import org.springframework.boot.ConfigurableBootstrapContext;
|
||||
import org.springframework.boot.context.config.ConfigDataEnvironmentContributor.ImportPhase;
|
||||
import org.springframework.boot.context.config.ConfigDataEnvironmentContributor.Kind;
|
||||
import org.springframework.boot.context.properties.bind.BindContext;
|
||||
|
|
@ -37,7 +38,6 @@ import org.springframework.boot.context.properties.bind.Binder;
|
|||
import org.springframework.boot.context.properties.bind.PlaceholdersResolver;
|
||||
import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
|
||||
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
|
||||
import org.springframework.boot.env.BootstrapRegistry;
|
||||
import org.springframework.boot.logging.DeferredLogFactory;
|
||||
import org.springframework.boot.origin.Origin;
|
||||
import org.springframework.core.log.LogMessage;
|
||||
|
|
@ -56,25 +56,25 @@ class ConfigDataEnvironmentContributors implements Iterable<ConfigDataEnvironmen
|
|||
|
||||
private final ConfigDataEnvironmentContributor root;
|
||||
|
||||
private final BootstrapRegistry bootstrapRegistry;
|
||||
private final ConfigurableBootstrapContext bootstrapContext;
|
||||
|
||||
/**
|
||||
* Create a new {@link ConfigDataEnvironmentContributors} instance.
|
||||
* @param logFactory the log factory
|
||||
* @param bootstrapRegistry the bootstrap registry
|
||||
* @param bootstrapContext the bootstrap context
|
||||
* @param contributors the initial set of contributors
|
||||
*/
|
||||
ConfigDataEnvironmentContributors(DeferredLogFactory logFactory, BootstrapRegistry bootstrapRegistry,
|
||||
ConfigDataEnvironmentContributors(DeferredLogFactory logFactory, ConfigurableBootstrapContext bootstrapContext,
|
||||
List<ConfigDataEnvironmentContributor> contributors) {
|
||||
this.logger = logFactory.getLog(getClass());
|
||||
this.bootstrapRegistry = bootstrapRegistry;
|
||||
this.bootstrapContext = bootstrapContext;
|
||||
this.root = ConfigDataEnvironmentContributor.of(contributors);
|
||||
}
|
||||
|
||||
private ConfigDataEnvironmentContributors(Log logger, BootstrapRegistry bootstrapRegistry,
|
||||
private ConfigDataEnvironmentContributors(Log logger, ConfigurableBootstrapContext bootstrapContext,
|
||||
ConfigDataEnvironmentContributor root) {
|
||||
this.logger = logger;
|
||||
this.bootstrapRegistry = bootstrapRegistry;
|
||||
this.bootstrapContext = bootstrapContext;
|
||||
this.root = root;
|
||||
}
|
||||
|
||||
|
|
@ -107,7 +107,7 @@ class ConfigDataEnvironmentContributors implements Iterable<ConfigDataEnvironmen
|
|||
result, activationContext, true);
|
||||
Binder binder = new Binder(sources, placeholdersResolver, null, null, null);
|
||||
ConfigDataEnvironmentContributor bound = contributor.withBoundProperties(binder);
|
||||
result = new ConfigDataEnvironmentContributors(this.logger, this.bootstrapRegistry,
|
||||
result = new ConfigDataEnvironmentContributors(this.logger, this.bootstrapContext,
|
||||
result.getRoot().withReplacement(contributor, bound));
|
||||
continue;
|
||||
}
|
||||
|
|
@ -122,14 +122,14 @@ class ConfigDataEnvironmentContributors implements Iterable<ConfigDataEnvironmen
|
|||
+ imported.size() + " location " + ((imported.size() != 1) ? "s" : "") + imported.keySet()));
|
||||
ConfigDataEnvironmentContributor contributorAndChildren = contributor.withChildren(importPhase,
|
||||
asContributors(imported));
|
||||
result = new ConfigDataEnvironmentContributors(this.logger, this.bootstrapRegistry,
|
||||
result = new ConfigDataEnvironmentContributors(this.logger, this.bootstrapContext,
|
||||
result.getRoot().withReplacement(contributor, contributorAndChildren));
|
||||
processed++;
|
||||
}
|
||||
}
|
||||
|
||||
protected final BootstrapRegistry getBootstrapRegistry() {
|
||||
return this.bootstrapRegistry;
|
||||
protected final ConfigurableBootstrapContext getBootstrapContext() {
|
||||
return this.bootstrapContext;
|
||||
}
|
||||
|
||||
private ConfigDataEnvironmentContributor getNextToProcess(ConfigDataEnvironmentContributors contributors,
|
||||
|
|
@ -222,8 +222,8 @@ class ConfigDataEnvironmentContributors implements Iterable<ConfigDataEnvironmen
|
|||
}
|
||||
|
||||
@Override
|
||||
public BootstrapRegistry getBootstrapRegistry() {
|
||||
return this.contributors.getBootstrapRegistry();
|
||||
public ConfigurableBootstrapContext getBootstrapContext() {
|
||||
return this.contributors.getBootstrapContext();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -264,8 +264,8 @@ class ConfigDataEnvironmentContributors implements Iterable<ConfigDataEnvironmen
|
|||
}
|
||||
|
||||
@Override
|
||||
public BootstrapRegistry getBootstrapRegistry() {
|
||||
return this.contributors.getBootstrapRegistry();
|
||||
public ConfigurableBootstrapContext getBootstrapContext() {
|
||||
return this.contributors.getBootstrapContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -23,9 +23,9 @@ import java.util.function.Supplier;
|
|||
|
||||
import org.apache.commons.logging.Log;
|
||||
|
||||
import org.springframework.boot.ConfigurableBootstrapContext;
|
||||
import org.springframework.boot.DefaultBootstrapContext;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.env.BootstrapRegistry;
|
||||
import org.springframework.boot.env.DefaultBootstrapRegisty;
|
||||
import org.springframework.boot.env.EnvironmentPostProcessor;
|
||||
import org.springframework.boot.logging.DeferredLogFactory;
|
||||
import org.springframework.core.Ordered;
|
||||
|
|
@ -61,12 +61,13 @@ public class ConfigDataEnvironmentPostProcessor implements EnvironmentPostProces
|
|||
|
||||
private final Log logger;
|
||||
|
||||
private final BootstrapRegistry bootstrapRegistry;
|
||||
private final ConfigurableBootstrapContext bootstrapContext;
|
||||
|
||||
public ConfigDataEnvironmentPostProcessor(DeferredLogFactory logFactory, BootstrapRegistry bootstrapRegistry) {
|
||||
public ConfigDataEnvironmentPostProcessor(DeferredLogFactory logFactory,
|
||||
ConfigurableBootstrapContext bootstrapContext) {
|
||||
this.logFactory = logFactory;
|
||||
this.logger = logFactory.getLog(getClass());
|
||||
this.bootstrapRegistry = bootstrapRegistry;
|
||||
this.bootstrapContext = bootstrapContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -95,7 +96,7 @@ public class ConfigDataEnvironmentPostProcessor implements EnvironmentPostProces
|
|||
|
||||
ConfigDataEnvironment getConfigDataEnvironment(ConfigurableEnvironment environment, ResourceLoader resourceLoader,
|
||||
Collection<String> additionalProfiles) {
|
||||
return new ConfigDataEnvironment(this.logFactory, this.bootstrapRegistry, environment, resourceLoader,
|
||||
return new ConfigDataEnvironment(this.logFactory, this.bootstrapContext, environment, resourceLoader,
|
||||
additionalProfiles);
|
||||
}
|
||||
|
||||
|
|
@ -125,13 +126,13 @@ public class ConfigDataEnvironmentPostProcessor implements EnvironmentPostProces
|
|||
* directly and not necessarily as part of a {@link SpringApplication}.
|
||||
* @param environment the environment to apply {@link ConfigData} to
|
||||
* @param resourceLoader the resource loader to use
|
||||
* @param bootstrapRegistry the bootstrap registry to use or {@code null} to use a
|
||||
* throw-away registry
|
||||
* @param bootstrapContext the bootstrap context to use or {@code null} to use a
|
||||
* throw-away context
|
||||
* @param additionalProfiles any additional profiles that should be applied
|
||||
*/
|
||||
public static void applyTo(ConfigurableEnvironment environment, ResourceLoader resourceLoader,
|
||||
BootstrapRegistry bootstrapRegistry, String... additionalProfiles) {
|
||||
applyTo(environment, resourceLoader, bootstrapRegistry, Arrays.asList(additionalProfiles));
|
||||
ConfigurableBootstrapContext bootstrapContext, String... additionalProfiles) {
|
||||
applyTo(environment, resourceLoader, bootstrapContext, Arrays.asList(additionalProfiles));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -140,16 +141,16 @@ public class ConfigDataEnvironmentPostProcessor implements EnvironmentPostProces
|
|||
* directly and not necessarily as part of a {@link SpringApplication}.
|
||||
* @param environment the environment to apply {@link ConfigData} to
|
||||
* @param resourceLoader the resource loader to use
|
||||
* @param bootstrapRegistry the bootstrap registry to use or {@code null} to use a
|
||||
* throw-away registry
|
||||
* @param bootstrapContext the bootstrap context to use or {@code null} to use a
|
||||
* throw-away context
|
||||
* @param additionalProfiles any additional profiles that should be applied
|
||||
*/
|
||||
public static void applyTo(ConfigurableEnvironment environment, ResourceLoader resourceLoader,
|
||||
BootstrapRegistry bootstrapRegistry, Collection<String> additionalProfiles) {
|
||||
ConfigurableBootstrapContext bootstrapContext, Collection<String> additionalProfiles) {
|
||||
DeferredLogFactory logFactory = Supplier::get;
|
||||
bootstrapRegistry = (bootstrapRegistry != null) ? bootstrapRegistry : new DefaultBootstrapRegisty();
|
||||
bootstrapContext = (bootstrapContext != null) ? bootstrapContext : new DefaultBootstrapContext();
|
||||
ConfigDataEnvironmentPostProcessor postProcessor = new ConfigDataEnvironmentPostProcessor(logFactory,
|
||||
bootstrapRegistry);
|
||||
bootstrapContext);
|
||||
postProcessor.postProcessEnvironment(environment, resourceLoader, additionalProfiles);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,10 @@ import java.io.IOException;
|
|||
|
||||
import org.apache.commons.logging.Log;
|
||||
|
||||
import org.springframework.boot.BootstrapContext;
|
||||
import org.springframework.boot.BootstrapRegistry;
|
||||
import org.springframework.boot.ConfigurableBootstrapContext;
|
||||
|
||||
/**
|
||||
* Strategy class that can be used used to load {@link ConfigData} instances from a
|
||||
* {@link ConfigDataLocation location}. Implementations should be added as a
|
||||
|
|
@ -27,6 +31,9 @@ import org.apache.commons.logging.Log;
|
|||
* supported:
|
||||
* <ul>
|
||||
* <li>{@link Log} - if the resolver needs deferred logging</li>
|
||||
* <li>{@link ConfigurableBootstrapContext} - A bootstrap context that can be used to
|
||||
* store objects that may be expensive to create, or need to be shared
|
||||
* ({@link BootstrapContext} or {@link BootstrapRegistry} may also be used).</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* Multiple loaders cannot claim the same location.
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
package org.springframework.boot.context.config;
|
||||
|
||||
import org.springframework.boot.env.BootstrapRegistry;
|
||||
import org.springframework.boot.ConfigurableBootstrapContext;
|
||||
import org.springframework.boot.env.EnvironmentPostProcessor;
|
||||
|
||||
/**
|
||||
|
|
@ -28,10 +28,10 @@ import org.springframework.boot.env.EnvironmentPostProcessor;
|
|||
public interface ConfigDataLoaderContext {
|
||||
|
||||
/**
|
||||
* Provides access to the {@link BootstrapRegistry} shared across all
|
||||
* Provides access to the {@link ConfigurableBootstrapContext} shared across all
|
||||
* {@link EnvironmentPostProcessor EnvironmentPostProcessors}.
|
||||
* @return the bootstrap registry
|
||||
* @return the bootstrap context
|
||||
*/
|
||||
BootstrapRegistry getBootstrapRegistry();
|
||||
ConfigurableBootstrapContext getBootstrapContext();
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,9 @@ import java.util.List;
|
|||
|
||||
import org.apache.commons.logging.Log;
|
||||
|
||||
import org.springframework.boot.BootstrapContext;
|
||||
import org.springframework.boot.BootstrapRegistry;
|
||||
import org.springframework.boot.ConfigurableBootstrapContext;
|
||||
import org.springframework.boot.logging.DeferredLogFactory;
|
||||
import org.springframework.boot.util.Instantiator;
|
||||
import org.springframework.core.ResolvableType;
|
||||
|
|
@ -48,27 +51,36 @@ class ConfigDataLoaders {
|
|||
|
||||
/**
|
||||
* Create a new {@link ConfigDataLoaders} instance.
|
||||
* @param logFactory the deferred log factory
|
||||
* @param bootstrapContext the bootstrap context
|
||||
* @param locationNotFoundAction the action to take if a
|
||||
* {@link ConfigDataLocationNotFoundException} is thrown
|
||||
* @param logFactory the deferred log factory
|
||||
*/
|
||||
ConfigDataLoaders(DeferredLogFactory logFactory, ConfigDataLocationNotFoundAction locationNotFoundAction) {
|
||||
this(logFactory, locationNotFoundAction, SpringFactoriesLoader.loadFactoryNames(ConfigDataLoader.class, null));
|
||||
ConfigDataLoaders(DeferredLogFactory logFactory, ConfigurableBootstrapContext bootstrapContext,
|
||||
ConfigDataLocationNotFoundAction locationNotFoundAction) {
|
||||
this(logFactory, bootstrapContext, locationNotFoundAction,
|
||||
SpringFactoriesLoader.loadFactoryNames(ConfigDataLoader.class, null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link ConfigDataLoaders} instance.
|
||||
* @param logFactory the deferred log factory
|
||||
* @param bootstrapContext the bootstrap context
|
||||
* @param locationNotFoundAction the action to take if a
|
||||
* {@link ConfigDataLocationNotFoundException} is thrown
|
||||
* @param names the {@link ConfigDataLoader} class names instantiate
|
||||
*/
|
||||
ConfigDataLoaders(DeferredLogFactory logFactory, ConfigDataLocationNotFoundAction locationNotFoundAction,
|
||||
List<String> names) {
|
||||
ConfigDataLoaders(DeferredLogFactory logFactory, ConfigurableBootstrapContext bootstrapContext,
|
||||
ConfigDataLocationNotFoundAction locationNotFoundAction, List<String> names) {
|
||||
this.logger = logFactory.getLog(getClass());
|
||||
this.locationNotFoundAction = locationNotFoundAction;
|
||||
Instantiator<ConfigDataLoader<?>> instantiator = new Instantiator<>(ConfigDataLoader.class,
|
||||
(availableParameters) -> availableParameters.add(Log.class, logFactory::getLog));
|
||||
(availableParameters) -> {
|
||||
availableParameters.add(Log.class, logFactory::getLog);
|
||||
availableParameters.add(ConfigurableBootstrapContext.class, bootstrapContext);
|
||||
availableParameters.add(BootstrapContext.class, bootstrapContext);
|
||||
availableParameters.add(BootstrapRegistry.class, bootstrapContext);
|
||||
});
|
||||
this.loaders = instantiator.instantiate(names);
|
||||
this.locationTypes = getLocationTypes(this.loaders);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,9 @@ import java.util.List;
|
|||
|
||||
import org.apache.commons.logging.Log;
|
||||
|
||||
import org.springframework.boot.BootstrapContext;
|
||||
import org.springframework.boot.BootstrapRegistry;
|
||||
import org.springframework.boot.ConfigurableBootstrapContext;
|
||||
import org.springframework.boot.context.properties.bind.Binder;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
|
|
@ -36,6 +39,9 @@ import org.springframework.core.io.ResourceLoader;
|
|||
* <li>{@link Binder} - if the resolver needs to obtain values from the initial
|
||||
* {@link Environment}</li>
|
||||
* <li>{@link ResourceLoader} - if the resolver needs a resource loader</li>
|
||||
* <li>{@link ConfigurableBootstrapContext} - A bootstrap context that can be used to
|
||||
* store objects that may be expensive to create, or need to be shared
|
||||
* ({@link BootstrapContext} or {@link BootstrapRegistry} may also be used).</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* Resolvers may implement {@link Ordered} or use the {@link Order @Order} annotation. The
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@
|
|||
|
||||
package org.springframework.boot.context.config;
|
||||
|
||||
import org.springframework.boot.ConfigurableBootstrapContext;
|
||||
import org.springframework.boot.context.properties.bind.Binder;
|
||||
import org.springframework.boot.env.BootstrapRegistry;
|
||||
import org.springframework.boot.env.EnvironmentPostProcessor;
|
||||
import org.springframework.boot.origin.Origin;
|
||||
|
||||
|
|
@ -45,11 +45,11 @@ public interface ConfigDataLocationResolverContext {
|
|||
ConfigDataLocation getParent();
|
||||
|
||||
/**
|
||||
* Provides access to the {@link BootstrapRegistry} shared across all
|
||||
* Provides access to the {@link ConfigurableBootstrapContext} shared across all
|
||||
* {@link EnvironmentPostProcessor EnvironmentPostProcessors}.
|
||||
* @return the bootstrap registry
|
||||
* @return the bootstrap context
|
||||
*/
|
||||
BootstrapRegistry getBootstrapRegistry();
|
||||
ConfigurableBootstrapContext getBootstrapContext();
|
||||
|
||||
/**
|
||||
* Return the {@link Origin} of a location that's being resolved.
|
||||
|
|
|
|||
|
|
@ -23,6 +23,9 @@ import java.util.function.Supplier;
|
|||
|
||||
import org.apache.commons.logging.Log;
|
||||
|
||||
import org.springframework.boot.BootstrapContext;
|
||||
import org.springframework.boot.BootstrapRegistry;
|
||||
import org.springframework.boot.ConfigurableBootstrapContext;
|
||||
import org.springframework.boot.context.properties.bind.Binder;
|
||||
import org.springframework.boot.logging.DeferredLogFactory;
|
||||
import org.springframework.boot.util.Instantiator;
|
||||
|
|
@ -50,28 +53,31 @@ class ConfigDataLocationResolvers {
|
|||
/**
|
||||
* Create a new {@link ConfigDataLocationResolvers} instance.
|
||||
* @param logFactory a {@link DeferredLogFactory} used to inject {@link Log} instances
|
||||
* @param bootstrapContext the bootstrap context
|
||||
* @param locationNotFoundAction the action to take if a
|
||||
* {@link ConfigDataLocationNotFoundException} is thrown
|
||||
* @param binder a binder providing values from the initial {@link Environment}
|
||||
* @param resourceLoader {@link ResourceLoader} to load resource locations
|
||||
*/
|
||||
ConfigDataLocationResolvers(DeferredLogFactory logFactory, ConfigDataLocationNotFoundAction locationNotFoundAction,
|
||||
Binder binder, ResourceLoader resourceLoader) {
|
||||
this(logFactory, locationNotFoundAction, binder, resourceLoader,
|
||||
ConfigDataLocationResolvers(DeferredLogFactory logFactory, ConfigurableBootstrapContext bootstrapContext,
|
||||
ConfigDataLocationNotFoundAction locationNotFoundAction, Binder binder, ResourceLoader resourceLoader) {
|
||||
this(logFactory, bootstrapContext, locationNotFoundAction, binder, resourceLoader,
|
||||
SpringFactoriesLoader.loadFactoryNames(ConfigDataLocationResolver.class, null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link ConfigDataLocationResolvers} instance.
|
||||
* @param logFactory a {@link DeferredLogFactory} used to inject {@link Log} instances
|
||||
* @param bootstrapContext the bootstrap context
|
||||
* @param locationNotFoundAction the action to take if a
|
||||
* {@link ConfigDataLocationNotFoundException} is thrown
|
||||
* @param binder {@link Binder} providing values from the initial {@link Environment}
|
||||
* @param resourceLoader {@link ResourceLoader} to load resource locations
|
||||
* @param names the {@link ConfigDataLocationResolver} class names
|
||||
*/
|
||||
ConfigDataLocationResolvers(DeferredLogFactory logFactory, ConfigDataLocationNotFoundAction locationNotFoundAction,
|
||||
Binder binder, ResourceLoader resourceLoader, List<String> names) {
|
||||
ConfigDataLocationResolvers(DeferredLogFactory logFactory, ConfigurableBootstrapContext bootstrapContext,
|
||||
ConfigDataLocationNotFoundAction locationNotFoundAction, Binder binder, ResourceLoader resourceLoader,
|
||||
List<String> names) {
|
||||
this.logger = logFactory.getLog(getClass());
|
||||
this.locationNotFoundAction = locationNotFoundAction;
|
||||
Instantiator<ConfigDataLocationResolver<?>> instantiator = new Instantiator<>(ConfigDataLocationResolver.class,
|
||||
|
|
@ -79,6 +85,9 @@ class ConfigDataLocationResolvers {
|
|||
availableParameters.add(Log.class, logFactory::getLog);
|
||||
availableParameters.add(Binder.class, binder);
|
||||
availableParameters.add(ResourceLoader.class, resourceLoader);
|
||||
availableParameters.add(ConfigurableBootstrapContext.class, bootstrapContext);
|
||||
availableParameters.add(BootstrapContext.class, bootstrapContext);
|
||||
availableParameters.add(BootstrapRegistry.class, bootstrapContext);
|
||||
});
|
||||
this.resolvers = reorder(instantiator.instantiate(names));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
* Copyright 2012-2020 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.
|
||||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package org.springframework.boot.context.event;
|
||||
|
||||
import org.springframework.boot.ConfigurableBootstrapContext;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
|
@ -30,6 +31,8 @@ import org.springframework.core.env.Environment;
|
|||
@SuppressWarnings("serial")
|
||||
public class ApplicationEnvironmentPreparedEvent extends SpringApplicationEvent {
|
||||
|
||||
private final ConfigurableBootstrapContext bootstrapContext;
|
||||
|
||||
private final ConfigurableEnvironment environment;
|
||||
|
||||
/**
|
||||
|
|
@ -37,13 +40,38 @@ public class ApplicationEnvironmentPreparedEvent extends SpringApplicationEvent
|
|||
* @param application the current application
|
||||
* @param args the arguments the application is running with
|
||||
* @param environment the environment that was just created
|
||||
* @deprecated since 2.4.0 in favor of
|
||||
* {@link #ApplicationEnvironmentPreparedEvent(ConfigurableBootstrapContext, SpringApplication, String[], ConfigurableEnvironment)}
|
||||
*/
|
||||
@Deprecated
|
||||
public ApplicationEnvironmentPreparedEvent(SpringApplication application, String[] args,
|
||||
ConfigurableEnvironment environment) {
|
||||
this(null, application, args, environment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link ApplicationEnvironmentPreparedEvent} instance.
|
||||
* @param bootstrapContext the bootstrap context
|
||||
* @param application the current application
|
||||
* @param args the arguments the application is running with
|
||||
* @param environment the environment that was just created
|
||||
*/
|
||||
public ApplicationEnvironmentPreparedEvent(ConfigurableBootstrapContext bootstrapContext,
|
||||
SpringApplication application, String[] args, ConfigurableEnvironment environment) {
|
||||
super(application, args);
|
||||
this.bootstrapContext = bootstrapContext;
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the bootstap context.
|
||||
* @return the bootstrap context
|
||||
* @since 2.4.0
|
||||
*/
|
||||
public ConfigurableBootstrapContext getBootstrapContext() {
|
||||
return this.bootstrapContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the environment.
|
||||
* @return the environment
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
* Copyright 2012-2020 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.
|
||||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package org.springframework.boot.context.event;
|
||||
|
||||
import org.springframework.boot.ConfigurableBootstrapContext;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
|
|
@ -35,13 +36,39 @@ import org.springframework.core.env.Environment;
|
|||
@SuppressWarnings("serial")
|
||||
public class ApplicationStartingEvent extends SpringApplicationEvent {
|
||||
|
||||
private final ConfigurableBootstrapContext bootstrapContext;
|
||||
|
||||
/**
|
||||
* Create a new {@link ApplicationStartingEvent} instance.
|
||||
* @param application the current application
|
||||
* @param args the arguments the application is running with
|
||||
* @deprecated since 2.4.0 in favor of
|
||||
* {@link #ApplicationStartingEvent(ConfigurableBootstrapContext, SpringApplication, String[])}
|
||||
*/
|
||||
@Deprecated
|
||||
public ApplicationStartingEvent(SpringApplication application, String[] args) {
|
||||
this(null, application, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link ApplicationStartingEvent} instance.
|
||||
* @param bootstrapContext the bootstrap context
|
||||
* @param application the current application
|
||||
* @param args the arguments the application is running with
|
||||
*/
|
||||
public ApplicationStartingEvent(ConfigurableBootstrapContext bootstrapContext, SpringApplication application,
|
||||
String[] args) {
|
||||
super(application, args);
|
||||
this.bootstrapContext = bootstrapContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the bootstap context.
|
||||
* @return the bootstrap context
|
||||
* @since 2.4.0
|
||||
*/
|
||||
public ConfigurableBootstrapContext getBootstrapContext() {
|
||||
return this.bootstrapContext;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ package org.springframework.boot.context.event;
|
|||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.boot.ConfigurableBootstrapContext;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.SpringApplicationRunListener;
|
||||
import org.springframework.boot.availability.AvailabilityChangeEvent;
|
||||
|
|
@ -70,14 +71,16 @@ public class EventPublishingRunListener implements SpringApplicationRunListener,
|
|||
}
|
||||
|
||||
@Override
|
||||
public void starting() {
|
||||
this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
|
||||
public void starting(ConfigurableBootstrapContext bootstrapContext) {
|
||||
this.initialMulticaster
|
||||
.multicastEvent(new ApplicationStartingEvent(bootstrapContext, this.application, this.args));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void environmentPrepared(ConfigurableEnvironment environment) {
|
||||
this.initialMulticaster
|
||||
.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
|
||||
public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext,
|
||||
ConfigurableEnvironment environment) {
|
||||
this.initialMulticaster.multicastEvent(
|
||||
new ApplicationEnvironmentPreparedEvent(bootstrapContext, this.application, this.args, environment));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -1,123 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2020 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
|
||||
*
|
||||
* https://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.boot.env;
|
||||
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.boot.context.event.ApplicationPreparedEvent;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
/**
|
||||
* A simple object registry that is available during {@link Environment} post-processing
|
||||
* up to the point that the {@link ApplicationContext} is prepared. The registry can be
|
||||
* used to store objects that may be expensive to create, or need to be shared by
|
||||
* different {@link EnvironmentPostProcessor EnvironmentPostProcessors}.
|
||||
* <p>
|
||||
* The registry uses the object type as a key, meaning that only a single instance of a
|
||||
* given class can be stored.
|
||||
* <p>
|
||||
* Registered instances may optionally use
|
||||
* {@link Registration#onApplicationContextPrepared(BiConsumer)
|
||||
* onApplicationContextPrepared(...)} to perform an action when the
|
||||
* {@link ApplicationContext} is {@link ApplicationPreparedEvent prepared}. For example,
|
||||
* an instance may choose to register itself as a regular Spring bean so that it is
|
||||
* available for the application to use.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 2.4.0
|
||||
* @see EnvironmentPostProcessor
|
||||
*/
|
||||
public interface BootstrapRegistry {
|
||||
|
||||
/**
|
||||
* Get an instance from the registry, creating one if it does not already exist.
|
||||
* @param <T> the instance type
|
||||
* @param type the instance type
|
||||
* @param instanceSupplier a supplier used to create the instance if it doesn't
|
||||
* already exist
|
||||
* @return the registered instance
|
||||
*/
|
||||
<T> T get(Class<T> type, Supplier<T> instanceSupplier);
|
||||
|
||||
/**
|
||||
* Get an instance from the registry, creating one if it does not already exist.
|
||||
* @param <T> the instance type
|
||||
* @param type the instance type
|
||||
* @param instanceSupplier a supplier used to create the instance if it doesn't
|
||||
* already exist
|
||||
* @param onApplicationContextPreparedAction the action that should be called when the
|
||||
* application context is prepared. This action is ignored if the registration already
|
||||
* exists.
|
||||
* @return the registered instance
|
||||
*/
|
||||
<T> T get(Class<T> type, Supplier<T> instanceSupplier,
|
||||
BiConsumer<ConfigurableApplicationContext, T> onApplicationContextPreparedAction);
|
||||
|
||||
/**
|
||||
* Register an instance with the registry and return a {@link Registration} that can
|
||||
* be used to provide further configuration. This method will replace any existing
|
||||
* registration.
|
||||
* @param <T> the instance type
|
||||
* @param type the instance type
|
||||
* @param instanceSupplier a supplier used to create the instance if it doesn't
|
||||
* already exist
|
||||
* @return an instance registration
|
||||
*/
|
||||
<T> Registration<T> register(Class<T> type, Supplier<T> instanceSupplier);
|
||||
|
||||
/**
|
||||
* Return if a registration exists for the given type.
|
||||
* @param <T> the instance type
|
||||
* @param type the instance type
|
||||
* @return {@code true} if the type has already been registered
|
||||
*/
|
||||
<T> boolean isRegistered(Class<T> type);
|
||||
|
||||
/**
|
||||
* Return any existing {@link Registration} for the given type.
|
||||
* @param <T> the instance type
|
||||
* @param type the instance type
|
||||
* @return the existing registration or {@code null}
|
||||
*/
|
||||
<T> Registration<T> getRegistration(Class<T> type);
|
||||
|
||||
/**
|
||||
* A single registration contained in the registry.
|
||||
*
|
||||
* @param <T> the instance type
|
||||
*/
|
||||
interface Registration<T> {
|
||||
|
||||
/**
|
||||
* Get or crearte the registered object instance.
|
||||
* @return the object instance
|
||||
*/
|
||||
T get();
|
||||
|
||||
/**
|
||||
* Add an action that should run when the {@link ApplicationContext} has been
|
||||
* prepared.
|
||||
* @param action the action to run
|
||||
*/
|
||||
void onApplicationContextPrepared(BiConsumer<ConfigurableApplicationContext, T> action);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,134 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2020 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
|
||||
*
|
||||
* https://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.boot.env;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link BootstrapRegistry}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 2.4.0
|
||||
*/
|
||||
public class DefaultBootstrapRegisty implements BootstrapRegistry {
|
||||
|
||||
private final Map<Class<?>, DefaultRegistration<?>> registrations = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public <T> T get(Class<T> type, Supplier<T> instanceSupplier) {
|
||||
return get(type, instanceSupplier, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T get(Class<T> type, Supplier<T> instanceSupplier,
|
||||
BiConsumer<ConfigurableApplicationContext, T> onApplicationContextPreparedAction) {
|
||||
Registration<T> registration = getRegistration(type);
|
||||
if (registration != null) {
|
||||
return registration.get();
|
||||
}
|
||||
registration = register(type, instanceSupplier);
|
||||
registration.onApplicationContextPrepared(onApplicationContextPreparedAction);
|
||||
return registration.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Registration<T> register(Class<T> type, Supplier<T> instanceSupplier) {
|
||||
DefaultRegistration<T> registration = new DefaultRegistration<>(instanceSupplier);
|
||||
this.registrations.put(type, registration);
|
||||
return registration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> boolean isRegistered(Class<T> type) {
|
||||
return getRegistration(type) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> Registration<T> getRegistration(Class<T> type) {
|
||||
return (Registration<T>) this.registrations.get(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to be called when the {@link ApplicationContext} is prepared.
|
||||
* @param applicationContext the prepared context
|
||||
*/
|
||||
public void applicationContextPrepared(ConfigurableApplicationContext applicationContext) {
|
||||
this.registrations.values()
|
||||
.forEach((registration) -> registration.applicationContextPrepared(applicationContext));
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the registry to reclaim memory.
|
||||
*/
|
||||
public void clear() {
|
||||
this.registrations.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Default implementation of {@link Registration}.
|
||||
*/
|
||||
private static class DefaultRegistration<T> implements Registration<T> {
|
||||
|
||||
private Supplier<T> instanceSupplier;
|
||||
|
||||
private volatile T instance;
|
||||
|
||||
private List<BiConsumer<ConfigurableApplicationContext, T>> applicationContextPreparedActions = new ArrayList<>();
|
||||
|
||||
DefaultRegistration(Supplier<T> instanceSupplier) {
|
||||
this.instanceSupplier = instanceSupplier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get() {
|
||||
T instance = this.instance;
|
||||
if (instance == null) {
|
||||
synchronized (this.instanceSupplier) {
|
||||
instance = this.instanceSupplier.get();
|
||||
this.instance = instance;
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationContextPrepared(BiConsumer<ConfigurableApplicationContext, T> action) {
|
||||
if (action != null) {
|
||||
this.applicationContextPreparedActions.add(action);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method called when the {@link ApplicationContext} is prepared.
|
||||
* @param applicationContext the prepared context
|
||||
*/
|
||||
void applicationContextPrepared(ConfigurableApplicationContext applicationContext) {
|
||||
this.applicationContextPreparedActions.forEach((consumer) -> consumer.accept(applicationContext, get()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -18,6 +18,9 @@ package org.springframework.boot.env;
|
|||
|
||||
import org.apache.commons.logging.Log;
|
||||
|
||||
import org.springframework.boot.BootstrapContext;
|
||||
import org.springframework.boot.BootstrapRegistry;
|
||||
import org.springframework.boot.ConfigurableBootstrapContext;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.logging.DeferredLogFactory;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
|
|
@ -41,8 +44,9 @@ import org.springframework.core.env.Environment;
|
|||
* itself to configure logging levels).</li>
|
||||
* <li>{@link Log} - A log with output deferred until the application has been full
|
||||
* prepared (allowing the environment itself to configure logging levels).</li>
|
||||
* <li>{@link BootstrapRegistry} - A bootstrap registry that can be used to store objects
|
||||
* that may be expensive to create, or need to be shared.</li>
|
||||
* <li>{@link ConfigurableBootstrapContext} - A bootstrap context that can be used to
|
||||
* store objects that may be expensive to create, or need to be shared
|
||||
* ({@link BootstrapContext} or {@link BootstrapRegistry} may also be used).</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package org.springframework.boot.env;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.boot.ConfigurableBootstrapContext;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
|
||||
import org.springframework.boot.context.event.ApplicationFailedEvent;
|
||||
|
|
@ -44,8 +45,6 @@ public class EnvironmentPostProcessorApplicationListener implements SmartApplica
|
|||
|
||||
private final DeferredLogs deferredLogs;
|
||||
|
||||
private final DefaultBootstrapRegisty bootstrapRegistry;
|
||||
|
||||
private int order = DEFAULT_ORDER;
|
||||
|
||||
private final EnvironmentPostProcessorsFactory postProcessorsFactory;
|
||||
|
|
@ -65,14 +64,13 @@ public class EnvironmentPostProcessorApplicationListener implements SmartApplica
|
|||
* @param postProcessorsFactory the post processors factory
|
||||
*/
|
||||
public EnvironmentPostProcessorApplicationListener(EnvironmentPostProcessorsFactory postProcessorsFactory) {
|
||||
this(postProcessorsFactory, new DeferredLogs(), new DefaultBootstrapRegisty());
|
||||
this(postProcessorsFactory, new DeferredLogs());
|
||||
}
|
||||
|
||||
EnvironmentPostProcessorApplicationListener(EnvironmentPostProcessorsFactory postProcessorsFactory,
|
||||
DeferredLogs deferredLogs, DefaultBootstrapRegisty bootstrapRegistry) {
|
||||
DeferredLogs deferredLogs) {
|
||||
this.postProcessorsFactory = postProcessorsFactory;
|
||||
this.deferredLogs = deferredLogs;
|
||||
this.bootstrapRegistry = bootstrapRegistry;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -98,13 +96,12 @@ public class EnvironmentPostProcessorApplicationListener implements SmartApplica
|
|||
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
|
||||
ConfigurableEnvironment environment = event.getEnvironment();
|
||||
SpringApplication application = event.getSpringApplication();
|
||||
for (EnvironmentPostProcessor postProcessor : getEnvironmentPostProcessors()) {
|
||||
for (EnvironmentPostProcessor postProcessor : getEnvironmentPostProcessors(event.getBootstrapContext())) {
|
||||
postProcessor.postProcessEnvironment(environment, application);
|
||||
}
|
||||
}
|
||||
|
||||
private void onApplicationPreparedEvent(ApplicationPreparedEvent event) {
|
||||
this.bootstrapRegistry.applicationContextPrepared(event.getApplicationContext());
|
||||
finish();
|
||||
}
|
||||
|
||||
|
|
@ -113,12 +110,11 @@ public class EnvironmentPostProcessorApplicationListener implements SmartApplica
|
|||
}
|
||||
|
||||
private void finish() {
|
||||
this.bootstrapRegistry.clear();
|
||||
this.deferredLogs.switchOverAll();
|
||||
}
|
||||
|
||||
List<EnvironmentPostProcessor> getEnvironmentPostProcessors() {
|
||||
return this.postProcessorsFactory.getEnvironmentPostProcessors(this.deferredLogs, this.bootstrapRegistry);
|
||||
List<EnvironmentPostProcessor> getEnvironmentPostProcessors(ConfigurableBootstrapContext bootstrapContext) {
|
||||
return this.postProcessorsFactory.getEnvironmentPostProcessors(this.deferredLogs, bootstrapContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package org.springframework.boot.env;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.boot.ConfigurableBootstrapContext;
|
||||
import org.springframework.boot.logging.DeferredLogFactory;
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
|
||||
|
|
@ -34,11 +35,11 @@ public interface EnvironmentPostProcessorsFactory {
|
|||
/**
|
||||
* Create all requested {@link EnvironmentPostProcessor} instances.
|
||||
* @param logFactory a deferred log factory
|
||||
* @param bootstrapRegistry a bootstrap registry
|
||||
* @param bootstrapContext a bootstrap context
|
||||
* @return the post processor instances
|
||||
*/
|
||||
List<EnvironmentPostProcessor> getEnvironmentPostProcessors(DeferredLogFactory logFactory,
|
||||
BootstrapRegistry bootstrapRegistry);
|
||||
ConfigurableBootstrapContext bootstrapContext);
|
||||
|
||||
/**
|
||||
* Return a {@link EnvironmentPostProcessorsFactory} backed by
|
||||
|
|
|
|||
|
|
@ -21,6 +21,9 @@ import java.util.List;
|
|||
|
||||
import org.apache.commons.logging.Log;
|
||||
|
||||
import org.springframework.boot.BootstrapContext;
|
||||
import org.springframework.boot.BootstrapRegistry;
|
||||
import org.springframework.boot.ConfigurableBootstrapContext;
|
||||
import org.springframework.boot.logging.DeferredLogFactory;
|
||||
import org.springframework.boot.util.Instantiator;
|
||||
|
||||
|
|
@ -48,12 +51,14 @@ class ReflectionEnvironmentPostProcessorsFactory implements EnvironmentPostProce
|
|||
|
||||
@Override
|
||||
public List<EnvironmentPostProcessor> getEnvironmentPostProcessors(DeferredLogFactory logFactory,
|
||||
BootstrapRegistry bootstrapRegistry) {
|
||||
ConfigurableBootstrapContext bootstrapContext) {
|
||||
Instantiator<EnvironmentPostProcessor> instantiator = new Instantiator<>(EnvironmentPostProcessor.class,
|
||||
(parameters) -> {
|
||||
parameters.add(DeferredLogFactory.class, logFactory);
|
||||
parameters.add(Log.class, logFactory::getLog);
|
||||
parameters.add(BootstrapRegistry.class, bootstrapRegistry);
|
||||
parameters.add(ConfigurableBootstrapContext.class, bootstrapContext);
|
||||
parameters.add(BootstrapContext.class, bootstrapContext);
|
||||
parameters.add(BootstrapRegistry.class, bootstrapContext);
|
||||
});
|
||||
return instantiator.instantiate(this.classNames);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,223 @@
|
|||
/*
|
||||
* Copyright 2012-2020 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
|
||||
*
|
||||
* https://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.boot;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.assertj.core.api.AbstractAssert;
|
||||
import org.assertj.core.api.AssertProvider;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.BootstrapRegistry.InstanceSupplier;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.support.StaticApplicationContext;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
|
||||
/**
|
||||
* Tests for {@link DefaultBootstrapContext}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class DefaultBootstrapContextTests {
|
||||
|
||||
private DefaultBootstrapContext context = new DefaultBootstrapContext();
|
||||
|
||||
private AtomicInteger counter = new AtomicInteger();
|
||||
|
||||
private StaticApplicationContext applicationContext = new StaticApplicationContext();
|
||||
|
||||
@Test
|
||||
void registerWhenTypeIsNullThrowsException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.context.register(null, InstanceSupplier.of(1)))
|
||||
.withMessage("Type must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerWhenRegistrationIsNullThrowException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.context.register(Integer.class, null))
|
||||
.withMessage("InstanceSupplier must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerWhenNotAlreadyRegisteredRegistersInstance() {
|
||||
this.context.register(Integer.class, InstanceSupplier.from(this.counter::getAndIncrement));
|
||||
assertThat(this.context.get(Integer.class)).isEqualTo(0);
|
||||
assertThat(this.context.get(Integer.class)).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerWhenAlreadyRegisteredRegistersReplacedInstance() {
|
||||
this.context.register(Integer.class, InstanceSupplier.from(this.counter::getAndIncrement));
|
||||
this.context.register(Integer.class, InstanceSupplier.of(100));
|
||||
assertThat(this.context.get(Integer.class)).isEqualTo(100);
|
||||
assertThat(this.context.get(Integer.class)).isEqualTo(100);
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerWhenAlreadyCreatedThrowsException() {
|
||||
this.context.register(Integer.class, InstanceSupplier.from(this.counter::getAndIncrement));
|
||||
this.context.get(Integer.class);
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> this.context.register(Integer.class, InstanceSupplier.of(100)))
|
||||
.withMessage("java.lang.Integer has already been created");
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerWithDependencyRegistersInstance() {
|
||||
this.context.register(Integer.class, InstanceSupplier.of(100));
|
||||
this.context.register(String.class, this::integerAsString);
|
||||
assertThat(this.context.get(String.class)).isEqualTo("100");
|
||||
}
|
||||
|
||||
private String integerAsString(BootstrapContext context) {
|
||||
return String.valueOf(context.get(Integer.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerIfAbsentWhenAbsentRegisters() {
|
||||
this.context.registerIfAbsent(Long.class, InstanceSupplier.of(100L));
|
||||
assertThat(this.context.get(Long.class)).isEqualTo(100L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerIfAbsentWhenPresentDoesNotRegister() {
|
||||
this.context.registerIfAbsent(Long.class, InstanceSupplier.of(1L));
|
||||
this.context.registerIfAbsent(Long.class, InstanceSupplier.of(100L));
|
||||
assertThat(this.context.get(Long.class)).isEqualTo(1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isRegisteredWhenNotRegisteredReturnsFalse() {
|
||||
this.context.register(Number.class, InstanceSupplier.of(1));
|
||||
assertThat(this.context.isRegistered(Long.class)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isRegisteredWhenRegisteredReturnsTrue() {
|
||||
this.context.register(Number.class, InstanceSupplier.of(1));
|
||||
assertThat(this.context.isRegistered(Number.class)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getRegisteredInstanceSupplierWhenNotRegisteredReturnsNull() {
|
||||
this.context.register(Number.class, InstanceSupplier.of(1));
|
||||
assertThat(this.context.getRegisteredInstanceSupplier(Long.class)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getRegisteredInstanceSupplierWhenRegisteredReturnsRegistration() {
|
||||
InstanceSupplier<Number> instanceSupplier = InstanceSupplier.of(1);
|
||||
this.context.register(Number.class, instanceSupplier);
|
||||
assertThat(this.context.getRegisteredInstanceSupplier(Number.class)).isSameAs(instanceSupplier);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getWhenNoRegistrationThrowsIllegalStateException() {
|
||||
this.context.register(Number.class, InstanceSupplier.of(1));
|
||||
assertThatIllegalStateException().isThrownBy(() -> this.context.get(Long.class))
|
||||
.withMessageContaining("has not been registered");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getWhenRegisteredAsNullReturnsNull() {
|
||||
this.context.register(Number.class, InstanceSupplier.of(null));
|
||||
assertThat(this.context.get(Number.class)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getCreatesOnlyOneInstance() {
|
||||
this.context.register(Integer.class, InstanceSupplier.from(this.counter::getAndIncrement));
|
||||
assertThat(this.context.get(Integer.class)).isEqualTo(0);
|
||||
assertThat(this.context.get(Integer.class)).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void closeMulticastsEventToListeners() {
|
||||
TestCloseListener listener = new TestCloseListener();
|
||||
this.context.addCloseListener(listener);
|
||||
assertThat(listener).wasNotCalled();
|
||||
this.context.close(this.applicationContext);
|
||||
assertThat(listener).wasCalledOnlyOnce().hasBootstrapContextSameAs(this.context)
|
||||
.hasApplicationContextSameAs(this.applicationContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
void addCloseListenerIgnoresMultipleCallsWithSameListener() {
|
||||
TestCloseListener listener = new TestCloseListener();
|
||||
this.context.addCloseListener(listener);
|
||||
this.context.addCloseListener(listener);
|
||||
this.context.close(this.applicationContext);
|
||||
assertThat(listener).wasCalledOnlyOnce();
|
||||
}
|
||||
|
||||
private static class TestCloseListener
|
||||
implements ApplicationListener<BootstrapContextClosedEvent>, AssertProvider<CloseListenerAssert> {
|
||||
|
||||
private int called;
|
||||
|
||||
private BootstrapContext bootstrapContext;
|
||||
|
||||
private ConfigurableApplicationContext applicationContext;
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(BootstrapContextClosedEvent event) {
|
||||
this.called++;
|
||||
this.bootstrapContext = event.getBootstrapContext();
|
||||
this.applicationContext = event.getApplicationContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CloseListenerAssert assertThat() {
|
||||
return new CloseListenerAssert(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class CloseListenerAssert extends AbstractAssert<CloseListenerAssert, TestCloseListener> {
|
||||
|
||||
CloseListenerAssert(TestCloseListener actual) {
|
||||
super(actual, CloseListenerAssert.class);
|
||||
}
|
||||
|
||||
CloseListenerAssert wasCalledOnlyOnce() {
|
||||
assertThat(this.actual.called).as("action calls").isEqualTo(1);
|
||||
return this;
|
||||
}
|
||||
|
||||
CloseListenerAssert wasNotCalled() {
|
||||
assertThat(this.actual.called).as("action calls").isEqualTo(0);
|
||||
return this;
|
||||
}
|
||||
|
||||
CloseListenerAssert hasBootstrapContextSameAs(BootstrapContext bootstrapContext) {
|
||||
assertThat(this.actual.bootstrapContext).isSameAs(bootstrapContext);
|
||||
return this;
|
||||
}
|
||||
|
||||
CloseListenerAssert hasApplicationContextSameAs(ApplicationContext applicationContext) {
|
||||
assertThat(this.actual.applicationContext).isSameAs(applicationContext);
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -48,6 +48,7 @@ import org.springframework.beans.factory.support.BeanDefinitionOverrideException
|
|||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.BeanNameGenerator;
|
||||
import org.springframework.beans.factory.support.DefaultBeanNameGenerator;
|
||||
import org.springframework.boot.BootstrapRegistry.InstanceSupplier;
|
||||
import org.springframework.boot.availability.AvailabilityChangeEvent;
|
||||
import org.springframework.boot.availability.AvailabilityState;
|
||||
import org.springframework.boot.availability.LivenessState;
|
||||
|
|
@ -105,6 +106,8 @@ import org.springframework.core.metrics.ApplicationStartup;
|
|||
import org.springframework.core.metrics.StartupStep;
|
||||
import org.springframework.http.server.reactive.HttpHandler;
|
||||
import org.springframework.test.context.support.TestPropertySourceUtils;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.context.ConfigurableWebEnvironment;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
|
|
@ -1201,6 +1204,35 @@ class SpringApplicationTests {
|
|||
assertThat(startCount).isEqualTo(endCount);
|
||||
}
|
||||
|
||||
@Test
|
||||
void addBootstrapper() {
|
||||
SpringApplication application = new SpringApplication(ExampleConfig.class);
|
||||
application.setWebApplicationType(WebApplicationType.NONE);
|
||||
application.addBootstrapper(
|
||||
(bootstrapContext) -> bootstrapContext.register(String.class, InstanceSupplier.of("boot")));
|
||||
TestApplicationListener listener = new TestApplicationListener();
|
||||
application.addListeners(listener);
|
||||
application.run();
|
||||
ApplicationStartingEvent startingEvent = listener.getEvent(ApplicationStartingEvent.class);
|
||||
assertThat(startingEvent.getBootstrapContext().get(String.class));
|
||||
ApplicationEnvironmentPreparedEvent environmentPreparedEvent = listener
|
||||
.getEvent(ApplicationEnvironmentPreparedEvent.class);
|
||||
assertThat(environmentPreparedEvent.getBootstrapContext().get(String.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void addBootstrapperCanRegisterBeans() {
|
||||
SpringApplication application = new SpringApplication(ExampleConfig.class);
|
||||
application.setWebApplicationType(WebApplicationType.NONE);
|
||||
application.addBootstrapper((bootstrapContext) -> {
|
||||
bootstrapContext.register(String.class, InstanceSupplier.of("boot"));
|
||||
bootstrapContext.addCloseListener((event) -> event.getApplicationContext().getBeanFactory()
|
||||
.registerSingleton("test", event.getBootstrapContext().get(String.class)));
|
||||
});
|
||||
ConfigurableApplicationContext applicationContext = application.run();
|
||||
assertThat(applicationContext.getBean("test")).isEqualTo("boot");
|
||||
}
|
||||
|
||||
private <S extends AvailabilityState> ArgumentMatcher<ApplicationEvent> isAvailabilityChangeEventWithState(
|
||||
S state) {
|
||||
return (argument) -> (argument instanceof AvailabilityChangeEvent<?>)
|
||||
|
|
@ -1658,4 +1690,20 @@ class SpringApplicationTests {
|
|||
|
||||
}
|
||||
|
||||
static class TestApplicationListener implements ApplicationListener<ApplicationEvent> {
|
||||
|
||||
private final MultiValueMap<Class<?>, ApplicationEvent> events = new LinkedMultiValueMap<>();
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(ApplicationEvent event) {
|
||||
this.events.add(event.getClass(), event);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
<E extends ApplicationEvent> E getEvent(Class<E> type) {
|
||||
return (E) this.events.get(type).get(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -282,6 +282,15 @@ class SpringApplicationBuilderTests {
|
|||
this.context.getBean(ChildConfig.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void addBootstrapper() {
|
||||
SpringApplicationBuilder application = new SpringApplicationBuilder(ExampleConfig.class)
|
||||
.web(WebApplicationType.NONE).addBootstrapper((context) -> context.addCloseListener(
|
||||
(event) -> event.getApplicationContext().getBeanFactory().registerSingleton("test", "spring")));
|
||||
this.context = application.run();
|
||||
assertThat(this.context.getBean("test")).isEqualTo("spring");
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class ExampleConfig {
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
* Copyright 2012-2020 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.
|
||||
|
|
@ -25,6 +25,7 @@ import org.junit.jupiter.api.BeforeEach;
|
|||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
import org.springframework.boot.DefaultBootstrapContext;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
|
||||
import org.springframework.boot.context.event.ApplicationPreparedEvent;
|
||||
|
|
@ -123,7 +124,8 @@ class ApplicationPidFileWriterTests {
|
|||
File file = new File(this.tempDir, "pid");
|
||||
ApplicationPidFileWriter listener = new ApplicationPidFileWriter(file);
|
||||
listener.setTriggerEventType(ApplicationStartingEvent.class);
|
||||
listener.onApplicationEvent(new ApplicationStartingEvent(new SpringApplication(), new String[] {}));
|
||||
listener.onApplicationEvent(
|
||||
new ApplicationStartingEvent(new DefaultBootstrapContext(), new SpringApplication(), new String[] {}));
|
||||
assertThat(contentOf(file)).isNotEmpty();
|
||||
}
|
||||
|
||||
|
|
@ -170,7 +172,8 @@ class ApplicationPidFileWriterTests {
|
|||
|
||||
private SpringApplicationEvent createEnvironmentPreparedEvent(String propName, String propValue) {
|
||||
ConfigurableEnvironment environment = createEnvironment(propName, propValue);
|
||||
return new ApplicationEnvironmentPreparedEvent(new SpringApplication(), new String[] {}, environment);
|
||||
return new ApplicationEnvironmentPreparedEvent(new DefaultBootstrapContext(), new SpringApplication(),
|
||||
new String[] {}, environment);
|
||||
}
|
||||
|
||||
private SpringApplicationEvent createPreparedEvent(String propName, String propValue) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
* Copyright 2012-2020 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.
|
||||
|
|
@ -18,6 +18,7 @@ package org.springframework.boot.context;
|
|||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.DefaultBootstrapContext;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
|
||||
import org.springframework.boot.context.properties.source.ConfigurationPropertySources;
|
||||
|
|
@ -40,7 +41,7 @@ class FileEncodingApplicationListenerTests {
|
|||
private final ConfigurableEnvironment environment = new StandardEnvironment();
|
||||
|
||||
private final ApplicationEnvironmentPreparedEvent event = new ApplicationEnvironmentPreparedEvent(
|
||||
new SpringApplication(), new String[0], this.environment);
|
||||
new DefaultBootstrapContext(), new SpringApplication(), new String[0], this.environment);
|
||||
|
||||
@Test
|
||||
void testIllegalState() {
|
||||
|
|
|
|||
|
|
@ -30,13 +30,12 @@ import org.mockito.ArgumentCaptor;
|
|||
import org.mockito.Captor;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import org.springframework.boot.DefaultBootstrapContext;
|
||||
import org.springframework.boot.cloud.CloudPlatform;
|
||||
import org.springframework.boot.context.config.ConfigDataEnvironmentContributor.Kind;
|
||||
import org.springframework.boot.context.config.ConfigDataEnvironmentContributors.BinderOption;
|
||||
import org.springframework.boot.context.properties.bind.BindException;
|
||||
import org.springframework.boot.context.properties.bind.Binder;
|
||||
import org.springframework.boot.env.BootstrapRegistry;
|
||||
import org.springframework.boot.env.DefaultBootstrapRegisty;
|
||||
import org.springframework.boot.logging.DeferredLogFactory;
|
||||
import org.springframework.mock.env.MockEnvironment;
|
||||
import org.springframework.mock.env.MockPropertySource;
|
||||
|
|
@ -60,7 +59,7 @@ class ConfigDataEnvironmentContributorsTests {
|
|||
|
||||
private DeferredLogFactory logFactory = Supplier::get;
|
||||
|
||||
private BootstrapRegistry bootstrapRegistry = new DefaultBootstrapRegisty();
|
||||
private DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
|
||||
|
||||
private MockEnvironment environment;
|
||||
|
||||
|
|
@ -80,9 +79,10 @@ class ConfigDataEnvironmentContributorsTests {
|
|||
void setup() {
|
||||
this.environment = new MockEnvironment();
|
||||
this.binder = Binder.get(this.environment);
|
||||
ConfigDataLocationResolvers resolvers = new ConfigDataLocationResolvers(this.logFactory,
|
||||
ConfigDataLocationResolvers resolvers = new ConfigDataLocationResolvers(this.logFactory, this.bootstrapContext,
|
||||
ConfigDataLocationNotFoundAction.FAIL, this.binder, null);
|
||||
ConfigDataLoaders loaders = new ConfigDataLoaders(this.logFactory, ConfigDataLocationNotFoundAction.FAIL);
|
||||
ConfigDataLoaders loaders = new ConfigDataLoaders(this.logFactory, this.bootstrapContext,
|
||||
ConfigDataLocationNotFoundAction.FAIL);
|
||||
this.importer = new ConfigDataImporter(resolvers, loaders);
|
||||
this.activationContext = new ConfigDataActivationContext(CloudPlatform.KUBERNETES, null);
|
||||
}
|
||||
|
|
@ -91,7 +91,7 @@ class ConfigDataEnvironmentContributorsTests {
|
|||
void createCreatesWithInitialContributors() {
|
||||
ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport("test");
|
||||
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
|
||||
this.bootstrapRegistry, Arrays.asList(contributor));
|
||||
this.bootstrapContext, Arrays.asList(contributor));
|
||||
Iterator<ConfigDataEnvironmentContributor> iterator = contributors.iterator();
|
||||
assertThat(iterator.next()).isSameAs(contributor);
|
||||
assertThat(iterator.next().getKind()).isEqualTo(Kind.ROOT);
|
||||
|
|
@ -102,7 +102,7 @@ class ConfigDataEnvironmentContributorsTests {
|
|||
ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor
|
||||
.ofExisting(new MockPropertySource());
|
||||
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
|
||||
this.bootstrapRegistry, Arrays.asList(contributor));
|
||||
this.bootstrapContext, Arrays.asList(contributor));
|
||||
ConfigDataEnvironmentContributors withProcessedImports = contributors.withProcessedImports(this.importer,
|
||||
this.activationContext);
|
||||
assertThat(withProcessedImports).isSameAs(contributors);
|
||||
|
|
@ -119,7 +119,7 @@ class ConfigDataEnvironmentContributorsTests {
|
|||
.willReturn(imported);
|
||||
ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport("testimport");
|
||||
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
|
||||
this.bootstrapRegistry, Arrays.asList(contributor));
|
||||
this.bootstrapContext, Arrays.asList(contributor));
|
||||
ConfigDataEnvironmentContributors withProcessedImports = contributors.withProcessedImports(this.importer,
|
||||
this.activationContext);
|
||||
Iterator<ConfigDataEnvironmentContributor> iterator = withProcessedImports.iterator();
|
||||
|
|
@ -148,7 +148,7 @@ class ConfigDataEnvironmentContributorsTests {
|
|||
ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor
|
||||
.ofInitialImport("initialimport");
|
||||
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
|
||||
this.bootstrapRegistry, Arrays.asList(contributor));
|
||||
this.bootstrapContext, Arrays.asList(contributor));
|
||||
ConfigDataEnvironmentContributors withProcessedImports = contributors.withProcessedImports(this.importer,
|
||||
this.activationContext);
|
||||
Iterator<ConfigDataEnvironmentContributor> iterator = withProcessedImports.iterator();
|
||||
|
|
@ -174,7 +174,7 @@ class ConfigDataEnvironmentContributorsTests {
|
|||
.willReturn(imported);
|
||||
ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport("testimport");
|
||||
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
|
||||
this.bootstrapRegistry, Arrays.asList(existingContributor, contributor));
|
||||
this.bootstrapContext, Arrays.asList(existingContributor, contributor));
|
||||
contributors.withProcessedImports(this.importer, this.activationContext);
|
||||
verify(this.importer).resolveAndLoad(any(), this.locationResolverContext.capture(), any(), any());
|
||||
ConfigDataLocationResolverContext context = this.locationResolverContext.getValue();
|
||||
|
|
@ -200,7 +200,7 @@ class ConfigDataEnvironmentContributorsTests {
|
|||
ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor
|
||||
.ofInitialImport("initialimport");
|
||||
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
|
||||
this.bootstrapRegistry, Arrays.asList(contributor));
|
||||
this.bootstrapContext, Arrays.asList(contributor));
|
||||
contributors.withProcessedImports(this.importer, this.activationContext);
|
||||
verify(this.importer).resolveAndLoad(any(), this.locationResolverContext.capture(), any(), eq(secondLocations));
|
||||
ConfigDataLocationResolverContext context = this.locationResolverContext.getValue();
|
||||
|
|
@ -222,11 +222,11 @@ class ConfigDataEnvironmentContributorsTests {
|
|||
.willReturn(imported);
|
||||
ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport("testimport");
|
||||
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
|
||||
this.bootstrapRegistry, Arrays.asList(existingContributor, contributor));
|
||||
this.bootstrapContext, Arrays.asList(existingContributor, contributor));
|
||||
contributors.withProcessedImports(this.importer, this.activationContext);
|
||||
verify(this.importer).resolveAndLoad(any(), this.locationResolverContext.capture(), any(), any());
|
||||
ConfigDataLocationResolverContext context = this.locationResolverContext.getValue();
|
||||
assertThat(context.getBootstrapRegistry()).isSameAs(this.bootstrapRegistry);
|
||||
assertThat(context.getBootstrapContext()).isSameAs(this.bootstrapContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -244,11 +244,11 @@ class ConfigDataEnvironmentContributorsTests {
|
|||
.willReturn(imported);
|
||||
ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport("testimport");
|
||||
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
|
||||
this.bootstrapRegistry, Arrays.asList(existingContributor, contributor));
|
||||
this.bootstrapContext, Arrays.asList(existingContributor, contributor));
|
||||
contributors.withProcessedImports(this.importer, this.activationContext);
|
||||
verify(this.importer).resolveAndLoad(any(), any(), this.loaderContext.capture(), any());
|
||||
ConfigDataLoaderContext context = this.loaderContext.getValue();
|
||||
assertThat(context.getBootstrapRegistry()).isSameAs(this.bootstrapRegistry);
|
||||
assertThat(context.getBootstrapContext()).isSameAs(this.bootstrapContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -257,7 +257,7 @@ class ConfigDataEnvironmentContributorsTests {
|
|||
propertySource.setProperty("test", "springboot");
|
||||
ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofExisting(propertySource);
|
||||
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
|
||||
this.bootstrapRegistry, Arrays.asList(contributor));
|
||||
this.bootstrapContext, Arrays.asList(contributor));
|
||||
Binder binder = contributors.getBinder(this.activationContext);
|
||||
assertThat(binder.bind("test", String.class).get()).isEqualTo("springboot");
|
||||
}
|
||||
|
|
@ -272,7 +272,7 @@ class ConfigDataEnvironmentContributorsTests {
|
|||
ConfigDataEnvironmentContributor firstContributor = createBoundImportContributor(configData, 0);
|
||||
ConfigDataEnvironmentContributor secondContributor = createBoundImportContributor(configData, 1);
|
||||
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
|
||||
this.bootstrapRegistry, Arrays.asList(firstContributor, secondContributor));
|
||||
this.bootstrapContext, Arrays.asList(firstContributor, secondContributor));
|
||||
Binder binder = contributors.getBinder(this.activationContext);
|
||||
assertThat(binder.bind("test", String.class).get()).isEqualTo("one");
|
||||
}
|
||||
|
|
@ -288,7 +288,7 @@ class ConfigDataEnvironmentContributorsTests {
|
|||
ConfigDataEnvironmentContributor firstContributor = createBoundImportContributor(configData, 0);
|
||||
ConfigDataEnvironmentContributor secondContributor = createBoundImportContributor(configData, 1);
|
||||
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
|
||||
this.bootstrapRegistry, Arrays.asList(firstContributor, secondContributor));
|
||||
this.bootstrapContext, Arrays.asList(firstContributor, secondContributor));
|
||||
Binder binder = contributors.getBinder(this.activationContext);
|
||||
assertThat(binder.bind("test", String.class).get()).isEqualTo("two");
|
||||
}
|
||||
|
|
@ -300,7 +300,7 @@ class ConfigDataEnvironmentContributorsTests {
|
|||
propertySource.setProperty("other", "springboot");
|
||||
ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofExisting(propertySource);
|
||||
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
|
||||
this.bootstrapRegistry, Arrays.asList(contributor));
|
||||
this.bootstrapContext, Arrays.asList(contributor));
|
||||
Binder binder = contributors.getBinder(this.activationContext);
|
||||
assertThat(binder.bind("test", String.class).get()).isEqualTo("springboot");
|
||||
}
|
||||
|
|
@ -317,7 +317,7 @@ class ConfigDataEnvironmentContributorsTests {
|
|||
ConfigDataEnvironmentContributor firstContributor = createBoundImportContributor(configData, 0);
|
||||
ConfigDataEnvironmentContributor secondContributor = createBoundImportContributor(configData, 1);
|
||||
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
|
||||
this.bootstrapRegistry, Arrays.asList(firstContributor, secondContributor));
|
||||
this.bootstrapContext, Arrays.asList(firstContributor, secondContributor));
|
||||
Binder binder = contributors.getBinder(this.activationContext);
|
||||
assertThat(binder.bind("test", String.class).get()).isEqualTo("two");
|
||||
}
|
||||
|
|
@ -333,7 +333,7 @@ class ConfigDataEnvironmentContributorsTests {
|
|||
ConfigDataEnvironmentContributor firstContributor = createBoundImportContributor(configData, 0);
|
||||
ConfigDataEnvironmentContributor secondContributor = createBoundImportContributor(configData, 1);
|
||||
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
|
||||
this.bootstrapRegistry, Arrays.asList(firstContributor, secondContributor));
|
||||
this.bootstrapContext, Arrays.asList(firstContributor, secondContributor));
|
||||
Binder binder = contributors.getBinder(this.activationContext, BinderOption.FAIL_ON_BIND_TO_INACTIVE_SOURCE);
|
||||
assertThatExceptionOfType(BindException.class).isThrownBy(() -> binder.bind("test", String.class))
|
||||
.satisfies((ex) -> assertThat(ex.getCause()).isInstanceOf(InactiveConfigDataAccessException.class));
|
||||
|
|
@ -350,7 +350,7 @@ class ConfigDataEnvironmentContributorsTests {
|
|||
ConfigDataEnvironmentContributor firstContributor = createBoundImportContributor(configData, 0);
|
||||
ConfigDataEnvironmentContributor secondContributor = createBoundImportContributor(configData, 1);
|
||||
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
|
||||
this.bootstrapRegistry, Arrays.asList(firstContributor, secondContributor));
|
||||
this.bootstrapContext, Arrays.asList(firstContributor, secondContributor));
|
||||
Binder binder = contributors.getBinder(this.activationContext, BinderOption.FAIL_ON_BIND_TO_INACTIVE_SOURCE);
|
||||
assertThatExceptionOfType(BindException.class).isThrownBy(() -> binder.bind("test", String.class))
|
||||
.satisfies((ex) -> assertThat(ex.getCause()).isInstanceOf(InactiveConfigDataAccessException.class));
|
||||
|
|
@ -368,7 +368,7 @@ class ConfigDataEnvironmentContributorsTests {
|
|||
ConfigDataEnvironmentContributor firstContributor = createBoundImportContributor(configData, 0);
|
||||
ConfigDataEnvironmentContributor secondContributor = createBoundImportContributor(configData, 1);
|
||||
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
|
||||
this.bootstrapRegistry, Arrays.asList(firstContributor, secondContributor));
|
||||
this.bootstrapContext, Arrays.asList(firstContributor, secondContributor));
|
||||
Binder binder = contributors.getBinder(this.activationContext, BinderOption.FAIL_ON_BIND_TO_INACTIVE_SOURCE);
|
||||
assertThatExceptionOfType(BindException.class).isThrownBy(() -> binder.bind("test", String.class))
|
||||
.satisfies((ex) -> assertThat(ex.getCause()).isInstanceOf(InactiveConfigDataAccessException.class));
|
||||
|
|
|
|||
|
|
@ -19,10 +19,10 @@ package org.springframework.boot.context.config;
|
|||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.BootstrapRegistry;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.WebApplicationType;
|
||||
import org.springframework.boot.context.config.TestConfigDataBootstrap.LoaderHelper;
|
||||
import org.springframework.boot.env.BootstrapRegistry;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
|
|
|
|||
|
|
@ -27,8 +27,8 @@ import org.mockito.Mock;
|
|||
import org.mockito.Spy;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import org.springframework.boot.DefaultBootstrapContext;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.env.DefaultBootstrapRegisty;
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
|
|
@ -60,7 +60,7 @@ class ConfigDataEnvironmentPostProcessorTests {
|
|||
|
||||
@Spy
|
||||
private ConfigDataEnvironmentPostProcessor postProcessor = new ConfigDataEnvironmentPostProcessor(Supplier::get,
|
||||
new DefaultBootstrapRegisty());
|
||||
new DefaultBootstrapContext());
|
||||
|
||||
@Captor
|
||||
private ArgumentCaptor<Set<String>> additionalProfilesCaptor;
|
||||
|
|
|
|||
|
|
@ -26,11 +26,11 @@ import org.junit.jupiter.api.Disabled;
|
|||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestInfo;
|
||||
|
||||
import org.springframework.boot.ConfigurableBootstrapContext;
|
||||
import org.springframework.boot.DefaultBootstrapContext;
|
||||
import org.springframework.boot.context.config.ConfigDataEnvironmentContributor.ImportPhase;
|
||||
import org.springframework.boot.context.config.ConfigDataEnvironmentContributor.Kind;
|
||||
import org.springframework.boot.context.properties.bind.Binder;
|
||||
import org.springframework.boot.env.BootstrapRegistry;
|
||||
import org.springframework.boot.env.DefaultBootstrapRegisty;
|
||||
import org.springframework.boot.logging.DeferredLogFactory;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
|
|
@ -52,7 +52,7 @@ class ConfigDataEnvironmentTests {
|
|||
|
||||
private DeferredLogFactory logFactory = Supplier::get;
|
||||
|
||||
private BootstrapRegistry bootstrapRegistry = new DefaultBootstrapRegisty();
|
||||
private DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
|
||||
|
||||
private MockEnvironment environment = new MockEnvironment();
|
||||
|
||||
|
|
@ -64,7 +64,7 @@ class ConfigDataEnvironmentTests {
|
|||
void createWhenUseLegacyPropertyInEnvironmentThrowsException() {
|
||||
this.environment.setProperty("spring.config.use-legacy-processing", "true");
|
||||
assertThatExceptionOfType(UseLegacyConfigProcessingException.class)
|
||||
.isThrownBy(() -> new ConfigDataEnvironment(this.logFactory, this.bootstrapRegistry, this.environment,
|
||||
.isThrownBy(() -> new ConfigDataEnvironment(this.logFactory, this.bootstrapContext, this.environment,
|
||||
this.resourceLoader, this.additionalProfiles));
|
||||
}
|
||||
|
||||
|
|
@ -72,7 +72,7 @@ class ConfigDataEnvironmentTests {
|
|||
void createExposesEnvironmentBinderToConfigDataLocationResolvers() {
|
||||
this.environment.setProperty("spring", "boot");
|
||||
TestConfigDataEnvironment configDataEnvironment = new TestConfigDataEnvironment(this.logFactory,
|
||||
this.bootstrapRegistry, this.environment, this.resourceLoader, this.additionalProfiles);
|
||||
this.bootstrapContext, this.environment, this.resourceLoader, this.additionalProfiles);
|
||||
assertThat(configDataEnvironment.getConfigDataLocationResolversBinder().bind("spring", String.class).get())
|
||||
.isEqualTo("boot");
|
||||
}
|
||||
|
|
@ -85,7 +85,7 @@ class ConfigDataEnvironmentTests {
|
|||
this.environment.getPropertySources().addLast(propertySource1);
|
||||
this.environment.getPropertySources().addLast(propertySource2);
|
||||
this.environment.getPropertySources().addLast(propertySource3);
|
||||
ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapRegistry,
|
||||
ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapContext,
|
||||
this.environment, this.resourceLoader, this.additionalProfiles);
|
||||
List<ConfigDataEnvironmentContributor> children = configDataEnvironment.getContributors().getRoot()
|
||||
.getChildren(ImportPhase.BEFORE_PROFILE_ACTIVATION);
|
||||
|
|
@ -104,7 +104,7 @@ class ConfigDataEnvironmentTests {
|
|||
this.environment.getPropertySources().addLast(defaultPropertySource);
|
||||
this.environment.getPropertySources().addLast(propertySource1);
|
||||
this.environment.getPropertySources().addLast(propertySource2);
|
||||
ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapRegistry,
|
||||
ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapContext,
|
||||
this.environment, this.resourceLoader, this.additionalProfiles);
|
||||
List<ConfigDataEnvironmentContributor> children = configDataEnvironment.getContributors().getRoot()
|
||||
.getChildren(ImportPhase.BEFORE_PROFILE_ACTIVATION);
|
||||
|
|
@ -120,7 +120,7 @@ class ConfigDataEnvironmentTests {
|
|||
this.environment.setProperty("spring.config.location", "l1,l2");
|
||||
this.environment.setProperty("spring.config.additional-location", "a1,a2");
|
||||
this.environment.setProperty("spring.config.import", "i1,i2");
|
||||
ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapRegistry,
|
||||
ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapContext,
|
||||
this.environment, this.resourceLoader, this.additionalProfiles);
|
||||
List<ConfigDataEnvironmentContributor> children = configDataEnvironment.getContributors().getRoot()
|
||||
.getChildren(ImportPhase.BEFORE_PROFILE_ACTIVATION);
|
||||
|
|
@ -132,7 +132,7 @@ class ConfigDataEnvironmentTests {
|
|||
@Test
|
||||
void processAndApplyAddsImportedSourceToEnvironment(TestInfo info) {
|
||||
this.environment.setProperty("spring.config.location", getConfigLocation(info));
|
||||
ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapRegistry,
|
||||
ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapContext,
|
||||
this.environment, this.resourceLoader, this.additionalProfiles);
|
||||
configDataEnvironment.processAndApply();
|
||||
assertThat(this.environment.getProperty("spring")).isEqualTo("boot");
|
||||
|
|
@ -141,7 +141,7 @@ class ConfigDataEnvironmentTests {
|
|||
@Test
|
||||
void processAndApplyOnlyAddsActiveContributors(TestInfo info) {
|
||||
this.environment.setProperty("spring.config.location", getConfigLocation(info));
|
||||
ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapRegistry,
|
||||
ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapContext,
|
||||
this.environment, this.resourceLoader, this.additionalProfiles);
|
||||
configDataEnvironment.processAndApply();
|
||||
assertThat(this.environment.getProperty("spring")).isEqualTo("boot");
|
||||
|
|
@ -153,7 +153,7 @@ class ConfigDataEnvironmentTests {
|
|||
MockPropertySource defaultPropertySource = new MockPropertySource("defaultProperties");
|
||||
this.environment.getPropertySources().addFirst(defaultPropertySource);
|
||||
this.environment.setProperty("spring.config.location", getConfigLocation(info));
|
||||
ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapRegistry,
|
||||
ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapContext,
|
||||
this.environment, this.resourceLoader, this.additionalProfiles);
|
||||
configDataEnvironment.processAndApply();
|
||||
List<PropertySource<?>> sources = this.environment.getPropertySources().stream().collect(Collectors.toList());
|
||||
|
|
@ -163,7 +163,7 @@ class ConfigDataEnvironmentTests {
|
|||
@Test
|
||||
void processAndApplySetsDefaultProfiles(TestInfo info) {
|
||||
this.environment.setProperty("spring.config.location", getConfigLocation(info));
|
||||
ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapRegistry,
|
||||
ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapContext,
|
||||
this.environment, this.resourceLoader, this.additionalProfiles);
|
||||
configDataEnvironment.processAndApply();
|
||||
assertThat(this.environment.getDefaultProfiles()).containsExactly("one", "two", "three");
|
||||
|
|
@ -172,7 +172,7 @@ class ConfigDataEnvironmentTests {
|
|||
@Test
|
||||
void processAndApplySetsActiveProfiles(TestInfo info) {
|
||||
this.environment.setProperty("spring.config.location", getConfigLocation(info));
|
||||
ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapRegistry,
|
||||
ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapContext,
|
||||
this.environment, this.resourceLoader, this.additionalProfiles);
|
||||
configDataEnvironment.processAndApply();
|
||||
assertThat(this.environment.getActiveProfiles()).containsExactly("one", "two", "three");
|
||||
|
|
@ -181,7 +181,7 @@ class ConfigDataEnvironmentTests {
|
|||
@Test
|
||||
void processAndApplySetsActiveProfilesAndProfileGroups(TestInfo info) {
|
||||
this.environment.setProperty("spring.config.location", getConfigLocation(info));
|
||||
ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapRegistry,
|
||||
ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapContext,
|
||||
this.environment, this.resourceLoader, this.additionalProfiles);
|
||||
configDataEnvironment.processAndApply();
|
||||
assertThat(this.environment.getActiveProfiles()).containsExactly("one", "four", "five", "two", "three");
|
||||
|
|
@ -191,7 +191,7 @@ class ConfigDataEnvironmentTests {
|
|||
@Disabled("Disabled until spring.profiles support is dropped")
|
||||
void processAndApplyWhenHasInvalidPropertyThrowsException() {
|
||||
this.environment.setProperty("spring.profile", "a");
|
||||
ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapRegistry,
|
||||
ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapContext,
|
||||
this.environment, this.resourceLoader, this.additionalProfiles);
|
||||
assertThatExceptionOfType(InvalidConfigDataPropertyException.class)
|
||||
.isThrownBy(() -> configDataEnvironment.processAndApply());
|
||||
|
|
@ -206,17 +206,19 @@ class ConfigDataEnvironmentTests {
|
|||
|
||||
private Binder configDataLocationResolversBinder;
|
||||
|
||||
TestConfigDataEnvironment(DeferredLogFactory logFactory, BootstrapRegistry bootstrapRegistry,
|
||||
TestConfigDataEnvironment(DeferredLogFactory logFactory, ConfigurableBootstrapContext bootstrapContext,
|
||||
ConfigurableEnvironment environment, ResourceLoader resourceLoader,
|
||||
Collection<String> additionalProfiles) {
|
||||
super(logFactory, bootstrapRegistry, environment, resourceLoader, additionalProfiles);
|
||||
super(logFactory, bootstrapContext, environment, resourceLoader, additionalProfiles);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConfigDataLocationResolvers createConfigDataLocationResolvers(DeferredLogFactory logFactory,
|
||||
ConfigDataLocationNotFoundAction locationNotFoundAction, Binder binder, ResourceLoader resourceLoader) {
|
||||
ConfigurableBootstrapContext bootstrapContext, ConfigDataLocationNotFoundAction locationNotFoundAction,
|
||||
Binder binder, ResourceLoader resourceLoader) {
|
||||
this.configDataLocationResolversBinder = binder;
|
||||
return super.createConfigDataLocationResolvers(logFactory, locationNotFoundAction, binder, resourceLoader);
|
||||
return super.createConfigDataLocationResolvers(logFactory, bootstrapContext, locationNotFoundAction, binder,
|
||||
resourceLoader);
|
||||
}
|
||||
|
||||
Binder getConfigDataLocationResolversBinder() {
|
||||
|
|
|
|||
|
|
@ -24,6 +24,11 @@ import java.util.function.Supplier;
|
|||
import org.apache.commons.logging.Log;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.BootstrapContext;
|
||||
import org.springframework.boot.BootstrapRegistry;
|
||||
import org.springframework.boot.BootstrapRegistry.InstanceSupplier;
|
||||
import org.springframework.boot.ConfigurableBootstrapContext;
|
||||
import org.springframework.boot.DefaultBootstrapContext;
|
||||
import org.springframework.boot.logging.DeferredLogFactory;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.mock.env.MockPropertySource;
|
||||
|
|
@ -42,19 +47,28 @@ class ConfigDataLoadersTests {
|
|||
|
||||
private DeferredLogFactory logFactory = Supplier::get;
|
||||
|
||||
private DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
|
||||
|
||||
private ConfigDataLoaderContext context = mock(ConfigDataLoaderContext.class);
|
||||
|
||||
@Test
|
||||
void createWhenLoaderHasLogParameterInjectsLog() {
|
||||
new ConfigDataLoaders(this.logFactory, ConfigDataLocationNotFoundAction.FAIL,
|
||||
new ConfigDataLoaders(this.logFactory, this.bootstrapContext, ConfigDataLocationNotFoundAction.FAIL,
|
||||
Arrays.asList(LoggingConfigDataLoader.class.getName()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void createWhenLoaderHasBootstrapParametersInjectsBootstrapContext() {
|
||||
new ConfigDataLoaders(this.logFactory, this.bootstrapContext, ConfigDataLocationNotFoundAction.FAIL,
|
||||
Arrays.asList(BootstrappingConfigDataLoader.class.getName()));
|
||||
assertThat(this.bootstrapContext.get(String.class)).isEqualTo("boot");
|
||||
}
|
||||
|
||||
@Test
|
||||
void loadWhenSingleLoaderSupportsLocationReturnsLoadedConfigData() throws Exception {
|
||||
TestConfigDataLocation location = new TestConfigDataLocation("test");
|
||||
ConfigDataLoaders loaders = new ConfigDataLoaders(this.logFactory, ConfigDataLocationNotFoundAction.FAIL,
|
||||
Arrays.asList(TestConfigDataLoader.class.getName()));
|
||||
ConfigDataLoaders loaders = new ConfigDataLoaders(this.logFactory, this.bootstrapContext,
|
||||
ConfigDataLocationNotFoundAction.FAIL, Arrays.asList(TestConfigDataLoader.class.getName()));
|
||||
ConfigData loaded = loaders.load(this.context, location);
|
||||
assertThat(getLoader(loaded)).isInstanceOf(TestConfigDataLoader.class);
|
||||
}
|
||||
|
|
@ -62,7 +76,8 @@ class ConfigDataLoadersTests {
|
|||
@Test
|
||||
void loadWhenMultipleLoadersSupportLocationThrowsException() throws Exception {
|
||||
TestConfigDataLocation location = new TestConfigDataLocation("test");
|
||||
ConfigDataLoaders loaders = new ConfigDataLoaders(this.logFactory, ConfigDataLocationNotFoundAction.FAIL,
|
||||
ConfigDataLoaders loaders = new ConfigDataLoaders(this.logFactory, this.bootstrapContext,
|
||||
ConfigDataLocationNotFoundAction.FAIL,
|
||||
Arrays.asList(LoggingConfigDataLoader.class.getName(), TestConfigDataLoader.class.getName()));
|
||||
assertThatIllegalStateException().isThrownBy(() -> loaders.load(this.context, location))
|
||||
.withMessageContaining("Multiple loaders found for location test");
|
||||
|
|
@ -71,8 +86,8 @@ class ConfigDataLoadersTests {
|
|||
@Test
|
||||
void loadWhenNoLoaderSupportsLocationThrowsException() {
|
||||
TestConfigDataLocation location = new TestConfigDataLocation("test");
|
||||
ConfigDataLoaders loaders = new ConfigDataLoaders(this.logFactory, ConfigDataLocationNotFoundAction.FAIL,
|
||||
Arrays.asList(NonLoadableConfigDataLoader.class.getName()));
|
||||
ConfigDataLoaders loaders = new ConfigDataLoaders(this.logFactory, this.bootstrapContext,
|
||||
ConfigDataLocationNotFoundAction.FAIL, Arrays.asList(NonLoadableConfigDataLoader.class.getName()));
|
||||
assertThatIllegalStateException().isThrownBy(() -> loaders.load(this.context, location))
|
||||
.withMessage("No loader found for location 'test'");
|
||||
}
|
||||
|
|
@ -80,7 +95,8 @@ class ConfigDataLoadersTests {
|
|||
@Test
|
||||
void loadWhenGenericTypeDoesNotMatchSkipsLoader() throws Exception {
|
||||
TestConfigDataLocation location = new TestConfigDataLocation("test");
|
||||
ConfigDataLoaders loaders = new ConfigDataLoaders(this.logFactory, ConfigDataLocationNotFoundAction.FAIL,
|
||||
ConfigDataLoaders loaders = new ConfigDataLoaders(this.logFactory, this.bootstrapContext,
|
||||
ConfigDataLocationNotFoundAction.FAIL,
|
||||
Arrays.asList(OtherConfigDataLoader.class.getName(), SpecificConfigDataLoader.class.getName()));
|
||||
ConfigData loaded = loaders.load(this.context, location);
|
||||
assertThat(getLoader(loaded)).isInstanceOf(SpecificConfigDataLoader.class);
|
||||
|
|
@ -131,6 +147,24 @@ class ConfigDataLoadersTests {
|
|||
|
||||
}
|
||||
|
||||
static class BootstrappingConfigDataLoader implements ConfigDataLoader<ConfigDataLocation> {
|
||||
|
||||
BootstrappingConfigDataLoader(ConfigurableBootstrapContext configurableBootstrapContext,
|
||||
BootstrapRegistry bootstrapRegistry, BootstrapContext bootstrapContext) {
|
||||
assertThat(configurableBootstrapContext).isNotNull();
|
||||
assertThat(bootstrapRegistry).isNotNull();
|
||||
assertThat(bootstrapContext).isNotNull();
|
||||
assertThat(configurableBootstrapContext).isEqualTo(bootstrapRegistry).isEqualTo(bootstrapContext);
|
||||
bootstrapRegistry.register(String.class, InstanceSupplier.of("boot"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigData load(ConfigDataLoaderContext context, ConfigDataLocation location) throws IOException {
|
||||
throw new AssertionError("Unexpected call");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class TestConfigDataLoader implements ConfigDataLoader<ConfigDataLocation> {
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -28,6 +28,11 @@ import org.junit.jupiter.api.extension.ExtendWith;
|
|||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import org.springframework.boot.BootstrapContext;
|
||||
import org.springframework.boot.BootstrapRegistry;
|
||||
import org.springframework.boot.BootstrapRegistry.InstanceSupplier;
|
||||
import org.springframework.boot.ConfigurableBootstrapContext;
|
||||
import org.springframework.boot.DefaultBootstrapContext;
|
||||
import org.springframework.boot.context.properties.bind.Binder;
|
||||
import org.springframework.boot.logging.DeferredLogFactory;
|
||||
import org.springframework.core.Ordered;
|
||||
|
|
@ -50,6 +55,8 @@ class ConfigDataLocationResolversTests {
|
|||
|
||||
private DeferredLogFactory logFactory = Supplier::get;
|
||||
|
||||
private DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
|
||||
|
||||
@Mock
|
||||
private Binder binder;
|
||||
|
||||
|
|
@ -63,7 +70,7 @@ class ConfigDataLocationResolversTests {
|
|||
|
||||
@Test
|
||||
void createWhenInjectingBinderCreatesResolver() {
|
||||
ConfigDataLocationResolvers resolvers = new ConfigDataLocationResolvers(this.logFactory,
|
||||
ConfigDataLocationResolvers resolvers = new ConfigDataLocationResolvers(this.logFactory, this.bootstrapContext,
|
||||
ConfigDataLocationNotFoundAction.FAIL, this.binder, this.resourceLoader,
|
||||
Collections.singletonList(TestBoundResolver.class.getName()));
|
||||
assertThat(resolvers.getResolvers()).hasSize(1);
|
||||
|
|
@ -73,17 +80,24 @@ class ConfigDataLocationResolversTests {
|
|||
|
||||
@Test
|
||||
void createWhenNotInjectingBinderCreatesResolver() {
|
||||
ConfigDataLocationResolvers resolvers = new ConfigDataLocationResolvers(this.logFactory,
|
||||
ConfigDataLocationResolvers resolvers = new ConfigDataLocationResolvers(this.logFactory, this.bootstrapContext,
|
||||
ConfigDataLocationNotFoundAction.FAIL, this.binder, this.resourceLoader,
|
||||
Collections.singletonList(TestResolver.class.getName()));
|
||||
assertThat(resolvers.getResolvers()).hasSize(1);
|
||||
assertThat(resolvers.getResolvers().get(0)).isExactlyInstanceOf(TestResolver.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void createWhenResolverHasBootstrapParametersInjectsBootstrapContext() {
|
||||
new ConfigDataLocationResolvers(this.logFactory, this.bootstrapContext, ConfigDataLocationNotFoundAction.FAIL,
|
||||
this.binder, this.resourceLoader, Collections.singletonList(TestBootstrappingResolver.class.getName()));
|
||||
assertThat(this.bootstrapContext.get(String.class)).isEqualTo("boot");
|
||||
}
|
||||
|
||||
@Test
|
||||
void createWhenNameIsNotConfigDataLocationResolverThrowsException() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> new ConfigDataLocationResolvers(this.logFactory,
|
||||
.isThrownBy(() -> new ConfigDataLocationResolvers(this.logFactory, this.bootstrapContext,
|
||||
ConfigDataLocationNotFoundAction.FAIL, this.binder, this.resourceLoader,
|
||||
Collections.singletonList(InputStream.class.getName())))
|
||||
.withMessageContaining("Unable to instantiate").havingCause().withMessageContaining("not assignable");
|
||||
|
|
@ -95,7 +109,7 @@ class ConfigDataLocationResolversTests {
|
|||
names.add(TestResolver.class.getName());
|
||||
names.add(LowestTestResolver.class.getName());
|
||||
names.add(HighestTestResolver.class.getName());
|
||||
ConfigDataLocationResolvers resolvers = new ConfigDataLocationResolvers(this.logFactory,
|
||||
ConfigDataLocationResolvers resolvers = new ConfigDataLocationResolvers(this.logFactory, this.bootstrapContext,
|
||||
ConfigDataLocationNotFoundAction.FAIL, this.binder, this.resourceLoader, names);
|
||||
assertThat(resolvers.getResolvers().get(0)).isExactlyInstanceOf(HighestTestResolver.class);
|
||||
assertThat(resolvers.getResolvers().get(1)).isExactlyInstanceOf(TestResolver.class);
|
||||
|
|
@ -104,7 +118,7 @@ class ConfigDataLocationResolversTests {
|
|||
|
||||
@Test
|
||||
void resolveAllResolvesUsingFirstSupportedResolver() {
|
||||
ConfigDataLocationResolvers resolvers = new ConfigDataLocationResolvers(this.logFactory,
|
||||
ConfigDataLocationResolvers resolvers = new ConfigDataLocationResolvers(this.logFactory, this.bootstrapContext,
|
||||
ConfigDataLocationNotFoundAction.FAIL, this.binder, this.resourceLoader,
|
||||
Arrays.asList(LowestTestResolver.class.getName(), HighestTestResolver.class.getName()));
|
||||
List<ConfigDataLocation> resolved = resolvers.resolveAll(this.context,
|
||||
|
|
@ -118,7 +132,7 @@ class ConfigDataLocationResolversTests {
|
|||
|
||||
@Test
|
||||
void resolveAllWhenProfileMergesResolvedLocations() {
|
||||
ConfigDataLocationResolvers resolvers = new ConfigDataLocationResolvers(this.logFactory,
|
||||
ConfigDataLocationResolvers resolvers = new ConfigDataLocationResolvers(this.logFactory, this.bootstrapContext,
|
||||
ConfigDataLocationNotFoundAction.FAIL, this.binder, this.resourceLoader,
|
||||
Arrays.asList(LowestTestResolver.class.getName(), HighestTestResolver.class.getName()));
|
||||
List<ConfigDataLocation> resolved = resolvers.resolveAll(this.context,
|
||||
|
|
@ -136,7 +150,7 @@ class ConfigDataLocationResolversTests {
|
|||
|
||||
@Test
|
||||
void resolveWhenNoResolverThrowsException() {
|
||||
ConfigDataLocationResolvers resolvers = new ConfigDataLocationResolvers(this.logFactory,
|
||||
ConfigDataLocationResolvers resolvers = new ConfigDataLocationResolvers(this.logFactory, this.bootstrapContext,
|
||||
ConfigDataLocationNotFoundAction.FAIL, this.binder, this.resourceLoader,
|
||||
Arrays.asList(LowestTestResolver.class.getName(), HighestTestResolver.class.getName()));
|
||||
assertThatExceptionOfType(UnsupportedConfigDataLocationException.class)
|
||||
|
|
@ -181,6 +195,19 @@ class ConfigDataLocationResolversTests {
|
|||
|
||||
}
|
||||
|
||||
static class TestBootstrappingResolver extends TestResolver {
|
||||
|
||||
TestBootstrappingResolver(ConfigurableBootstrapContext configurableBootstrapContext,
|
||||
BootstrapRegistry bootstrapRegistry, BootstrapContext bootstrapContext) {
|
||||
assertThat(configurableBootstrapContext).isNotNull();
|
||||
assertThat(bootstrapRegistry).isNotNull();
|
||||
assertThat(bootstrapContext).isNotNull();
|
||||
assertThat(configurableBootstrapContext).isEqualTo(bootstrapRegistry).isEqualTo(bootstrapContext);
|
||||
bootstrapRegistry.register(String.class, InstanceSupplier.of("boot"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
static class HighestTestResolver extends TestResolver {
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
* Copyright 2012-2020 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.
|
||||
|
|
@ -19,6 +19,7 @@ package org.springframework.boot.context.config;
|
|||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.DefaultBootstrapContext;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
|
|
@ -53,8 +54,8 @@ class DelegatingApplicationListenerTests {
|
|||
void orderedInitialize() {
|
||||
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.context,
|
||||
"context.listener.classes=" + MockInitB.class.getName() + "," + MockInitA.class.getName());
|
||||
this.listener.onApplicationEvent(new ApplicationEnvironmentPreparedEvent(new SpringApplication(), new String[0],
|
||||
this.context.getEnvironment()));
|
||||
this.listener.onApplicationEvent(new ApplicationEnvironmentPreparedEvent(new DefaultBootstrapContext(),
|
||||
new SpringApplication(), new String[0], this.context.getEnvironment()));
|
||||
this.context.getBeanFactory().registerSingleton("testListener", this.listener);
|
||||
this.context.refresh();
|
||||
assertThat(this.context.getBeanFactory().getSingleton("a")).isEqualTo("a");
|
||||
|
|
@ -63,15 +64,15 @@ class DelegatingApplicationListenerTests {
|
|||
|
||||
@Test
|
||||
void noInitializers() {
|
||||
this.listener.onApplicationEvent(new ApplicationEnvironmentPreparedEvent(new SpringApplication(), new String[0],
|
||||
this.context.getEnvironment()));
|
||||
this.listener.onApplicationEvent(new ApplicationEnvironmentPreparedEvent(new DefaultBootstrapContext(),
|
||||
new SpringApplication(), new String[0], this.context.getEnvironment()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void emptyInitializers() {
|
||||
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.context, "context.listener.classes:");
|
||||
this.listener.onApplicationEvent(new ApplicationEnvironmentPreparedEvent(new SpringApplication(), new String[0],
|
||||
this.context.getEnvironment()));
|
||||
this.listener.onApplicationEvent(new ApplicationEnvironmentPreparedEvent(new DefaultBootstrapContext(),
|
||||
new SpringApplication(), new String[0], this.context.getEnvironment()));
|
||||
}
|
||||
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
|
|
|
|||
|
|
@ -20,7 +20,9 @@ import java.io.IOException;
|
|||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.boot.BootstrapContextClosedEvent;
|
||||
import org.springframework.boot.BootstrapRegistry.InstanceSupplier;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
|
||||
/**
|
||||
|
|
@ -42,8 +44,9 @@ class TestConfigDataBootstrap {
|
|||
|
||||
@Override
|
||||
public List<Location> resolve(ConfigDataLocationResolverContext context, String location, boolean optional) {
|
||||
ResolverHelper helper = context.getBootstrapRegistry().get(ResolverHelper.class,
|
||||
() -> new ResolverHelper(location));
|
||||
context.getBootstrapContext().registerIfAbsent(ResolverHelper.class,
|
||||
InstanceSupplier.from(() -> new ResolverHelper(location)));
|
||||
ResolverHelper helper = context.getBootstrapContext().get(ResolverHelper.class);
|
||||
return Collections.singletonList(new Location(helper));
|
||||
}
|
||||
|
||||
|
|
@ -53,8 +56,10 @@ class TestConfigDataBootstrap {
|
|||
|
||||
@Override
|
||||
public ConfigData load(ConfigDataLoaderContext context, Location location) throws IOException {
|
||||
context.getBootstrapRegistry().get(LoaderHelper.class, () -> new LoaderHelper(location),
|
||||
LoaderHelper::addToContext);
|
||||
context.getBootstrapContext().registerIfAbsent(LoaderHelper.class,
|
||||
InstanceSupplier.from(() -> new LoaderHelper(location)));
|
||||
LoaderHelper helper = context.getBootstrapContext().get(LoaderHelper.class);
|
||||
context.getBootstrapContext().addCloseListener(helper);
|
||||
return new ConfigData(
|
||||
Collections.singleton(new MapPropertySource("loaded", Collections.singletonMap("test", "test"))));
|
||||
}
|
||||
|
|
@ -94,7 +99,7 @@ class TestConfigDataBootstrap {
|
|||
|
||||
}
|
||||
|
||||
static class LoaderHelper {
|
||||
static class LoaderHelper implements ApplicationListener<BootstrapContextClosedEvent> {
|
||||
|
||||
private final Location location;
|
||||
|
||||
|
|
@ -106,8 +111,9 @@ class TestConfigDataBootstrap {
|
|||
return this.location;
|
||||
}
|
||||
|
||||
static void addToContext(ConfigurableApplicationContext context, LoaderHelper loaderHelper) {
|
||||
context.getBeanFactory().registerSingleton("loaderHelper", loaderHelper);
|
||||
@Override
|
||||
public void onApplicationEvent(BootstrapContextClosedEvent event) {
|
||||
event.getApplicationContext().getBeanFactory().registerSingleton("loaderHelper", this);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import java.util.List;
|
|||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.DefaultBootstrapContext;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.availability.AvailabilityChangeEvent;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
|
@ -42,6 +43,8 @@ import static org.mockito.Mockito.mock;
|
|||
*/
|
||||
class EventPublishingRunListenerTests {
|
||||
|
||||
private DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
|
||||
|
||||
private SpringApplication application;
|
||||
|
||||
private EventPublishingRunListener runListener;
|
||||
|
|
@ -60,9 +63,9 @@ class EventPublishingRunListenerTests {
|
|||
void shouldPublishLifecycleEvents() {
|
||||
StaticApplicationContext context = new StaticApplicationContext();
|
||||
assertThat(this.eventListener.receivedEvents()).isEmpty();
|
||||
this.runListener.starting();
|
||||
this.runListener.starting(this.bootstrapContext);
|
||||
checkApplicationEvents(ApplicationStartingEvent.class);
|
||||
this.runListener.environmentPrepared(null);
|
||||
this.runListener.environmentPrepared(this.bootstrapContext, null);
|
||||
checkApplicationEvents(ApplicationEnvironmentPreparedEvent.class);
|
||||
this.runListener.contextPrepared(context);
|
||||
checkApplicationEvents(ApplicationContextInitializedEvent.class);
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ import org.junit.jupiter.api.io.TempDir;
|
|||
import org.slf4j.bridge.SLF4JBridgeHandler;
|
||||
import org.slf4j.impl.StaticLoggerBinder;
|
||||
|
||||
import org.springframework.boot.DefaultBootstrapContext;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.context.event.ApplicationFailedEvent;
|
||||
import org.springframework.boot.context.event.ApplicationStartingEvent;
|
||||
|
|
@ -97,6 +98,8 @@ class LoggingApplicationListenerTests {
|
|||
|
||||
private final ch.qos.logback.classic.Logger logger = this.loggerContext.getLogger(getClass());
|
||||
|
||||
private final DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
|
||||
|
||||
private final SpringApplication springApplication = new SpringApplication();
|
||||
|
||||
private final GenericApplicationContext context = new GenericApplicationContext();
|
||||
|
|
@ -113,7 +116,7 @@ class LoggingApplicationListenerTests {
|
|||
this.output = output;
|
||||
this.logFile = new File(this.tempDir.toFile(), "foo.log");
|
||||
LogManager.getLogManager().readConfiguration(JavaLoggingSystem.class.getResourceAsStream("logging.properties"));
|
||||
multicastEvent(new ApplicationStartingEvent(new SpringApplication(), NO_ARGS));
|
||||
multicastEvent(new ApplicationStartingEvent(this.bootstrapContext, new SpringApplication(), NO_ARGS));
|
||||
new File(this.tempDir.toFile(), "spring.log").delete();
|
||||
ConfigurableEnvironment environment = this.context.getEnvironment();
|
||||
ConfigurationPropertySources.attach(environment);
|
||||
|
|
@ -372,7 +375,8 @@ class LoggingApplicationListenerTests {
|
|||
void parseArgsDoesntReplace() {
|
||||
this.initializer.setSpringBootLogging(LogLevel.ERROR);
|
||||
this.initializer.setParseArgs(false);
|
||||
multicastEvent(new ApplicationStartingEvent(this.springApplication, new String[] { "--debug" }));
|
||||
multicastEvent(new ApplicationStartingEvent(this.bootstrapContext, this.springApplication,
|
||||
new String[] { "--debug" }));
|
||||
this.initializer.initialize(this.context.getEnvironment(), this.context.getClassLoader());
|
||||
this.logger.debug("testatdebug");
|
||||
assertThat(this.output).doesNotContain("testatdebug");
|
||||
|
|
@ -406,7 +410,7 @@ class LoggingApplicationListenerTests {
|
|||
void shutdownHookIsNotRegisteredByDefault() {
|
||||
TestLoggingApplicationListener listener = new TestLoggingApplicationListener();
|
||||
System.setProperty(LoggingSystem.class.getName(), TestShutdownHandlerLoggingSystem.class.getName());
|
||||
multicastEvent(listener, new ApplicationStartingEvent(new SpringApplication(), NO_ARGS));
|
||||
multicastEvent(listener, new ApplicationStartingEvent(this.bootstrapContext, new SpringApplication(), NO_ARGS));
|
||||
listener.initialize(this.context.getEnvironment(), this.context.getClassLoader());
|
||||
assertThat(listener.shutdownHook).isNull();
|
||||
}
|
||||
|
|
@ -416,7 +420,7 @@ class LoggingApplicationListenerTests {
|
|||
TestLoggingApplicationListener listener = new TestLoggingApplicationListener();
|
||||
System.setProperty(LoggingSystem.class.getName(), TestShutdownHandlerLoggingSystem.class.getName());
|
||||
addPropertiesToEnvironment(this.context, "logging.register_shutdown_hook=true");
|
||||
multicastEvent(listener, new ApplicationStartingEvent(new SpringApplication(), NO_ARGS));
|
||||
multicastEvent(listener, new ApplicationStartingEvent(this.bootstrapContext, new SpringApplication(), NO_ARGS));
|
||||
listener.initialize(this.context.getEnvironment(), this.context.getClassLoader());
|
||||
assertThat(listener.shutdownHook).isNotNull();
|
||||
listener.shutdownHook.start();
|
||||
|
|
@ -426,7 +430,7 @@ class LoggingApplicationListenerTests {
|
|||
@Test
|
||||
void closingContextCleansUpLoggingSystem() {
|
||||
System.setProperty(LoggingSystem.SYSTEM_PROPERTY, TestCleanupLoggingSystem.class.getName());
|
||||
multicastEvent(new ApplicationStartingEvent(this.springApplication, new String[0]));
|
||||
multicastEvent(new ApplicationStartingEvent(this.bootstrapContext, this.springApplication, new String[0]));
|
||||
TestCleanupLoggingSystem loggingSystem = (TestCleanupLoggingSystem) ReflectionTestUtils
|
||||
.getField(this.initializer, "loggingSystem");
|
||||
assertThat(loggingSystem.cleanedUp).isFalse();
|
||||
|
|
@ -437,7 +441,7 @@ class LoggingApplicationListenerTests {
|
|||
@Test
|
||||
void closingChildContextDoesNotCleanUpLoggingSystem() {
|
||||
System.setProperty(LoggingSystem.SYSTEM_PROPERTY, TestCleanupLoggingSystem.class.getName());
|
||||
multicastEvent(new ApplicationStartingEvent(this.springApplication, new String[0]));
|
||||
multicastEvent(new ApplicationStartingEvent(this.bootstrapContext, this.springApplication, new String[0]));
|
||||
TestCleanupLoggingSystem loggingSystem = (TestCleanupLoggingSystem) ReflectionTestUtils
|
||||
.getField(this.initializer, "loggingSystem");
|
||||
assertThat(loggingSystem.cleanedUp).isFalse();
|
||||
|
|
@ -496,7 +500,7 @@ class LoggingApplicationListenerTests {
|
|||
@Test
|
||||
void applicationFailedEventCleansUpLoggingSystem() {
|
||||
System.setProperty(LoggingSystem.SYSTEM_PROPERTY, TestCleanupLoggingSystem.class.getName());
|
||||
multicastEvent(new ApplicationStartingEvent(this.springApplication, new String[0]));
|
||||
multicastEvent(new ApplicationStartingEvent(this.bootstrapContext, this.springApplication, new String[0]));
|
||||
TestCleanupLoggingSystem loggingSystem = (TestCleanupLoggingSystem) ReflectionTestUtils
|
||||
.getField(this.initializer, "loggingSystem");
|
||||
assertThat(loggingSystem.cleanedUp).isFalse();
|
||||
|
|
|
|||
|
|
@ -1,199 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2020 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
|
||||
*
|
||||
* https://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.boot.env;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.assertj.core.api.AbstractAssert;
|
||||
import org.assertj.core.api.AssertProvider;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.env.BootstrapRegistry.Registration;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.support.StaticApplicationContext;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Test for {@link DefaultBootstrapRegisty}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class DefaultBootstrapRegistyTests {
|
||||
|
||||
private DefaultBootstrapRegisty registy = new DefaultBootstrapRegisty();
|
||||
|
||||
private AtomicInteger counter = new AtomicInteger();
|
||||
|
||||
private StaticApplicationContext context = new StaticApplicationContext();
|
||||
|
||||
@Test
|
||||
void getWhenNotRegisteredCreateInstance() {
|
||||
Integer result = this.registy.get(Integer.class, this.counter::getAndIncrement);
|
||||
assertThat(result).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getWhenAlreadyRegisteredReturnsExisting() {
|
||||
this.registy.get(Integer.class, this.counter::getAndIncrement);
|
||||
Integer result = this.registy.get(Integer.class, this.counter::getAndIncrement);
|
||||
assertThat(result).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getWithPreparedActionRegistersAction() {
|
||||
TestApplicationPreparedAction action = new TestApplicationPreparedAction();
|
||||
Integer result = this.registy.get(Integer.class, this.counter::getAndIncrement, action::run);
|
||||
this.registy.applicationContextPrepared(this.context);
|
||||
assertThat(result).isEqualTo(0);
|
||||
assertThat(action).wasCalledOnlyOnce().hasInstanceValue(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getWithPreparedActionWhenAlreadyRegisteredIgnoresRegistersAction() {
|
||||
TestApplicationPreparedAction action1 = new TestApplicationPreparedAction();
|
||||
TestApplicationPreparedAction action2 = new TestApplicationPreparedAction();
|
||||
this.registy.get(Integer.class, this.counter::getAndIncrement, action1::run);
|
||||
this.registy.get(Integer.class, this.counter::getAndIncrement, action2::run);
|
||||
this.registy.applicationContextPrepared(this.context);
|
||||
assertThat(action1).wasCalledOnlyOnce().hasInstanceValue(0);
|
||||
assertThat(action2).wasNotCalled();
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerAddsRegistration() {
|
||||
Registration<Integer> registration = this.registy.register(Integer.class, this.counter::getAndIncrement);
|
||||
assertThat(registration.get()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerWhenAlreadyRegisteredReplacesPreviousRegistration() {
|
||||
Registration<Integer> registration1 = this.registy.register(Integer.class, this.counter::getAndIncrement);
|
||||
Registration<Integer> registration2 = this.registy.register(Integer.class, () -> -1);
|
||||
assertThat(registration2).isNotEqualTo(registration1);
|
||||
assertThat(registration1.get()).isEqualTo(0);
|
||||
assertThat(registration2.get()).isEqualTo(-1);
|
||||
assertThat(this.registy.get(Integer.class, this.counter::getAndIncrement)).isEqualTo(-1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isRegisteredWhenNotRegisteredReturnsFalse() {
|
||||
assertThat(this.registy.isRegistered(Integer.class)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isRegisteredWhenRegisteredReturnsTrue() {
|
||||
this.registy.register(Integer.class, this.counter::getAndIncrement);
|
||||
assertThat(this.registy.isRegistered(Integer.class)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getRegistrationWhenNotRegisteredReturnsNull() {
|
||||
assertThat(this.registy.getRegistration(Integer.class)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getRegistrationWhenRegisteredReturnsRegistration() {
|
||||
Registration<Integer> registration = this.registy.register(Integer.class, this.counter::getAndIncrement);
|
||||
assertThat(this.registy.getRegistration(Integer.class)).isSameAs(registration);
|
||||
}
|
||||
|
||||
@Test
|
||||
void applicationContextPreparedTriggersActions() {
|
||||
TestApplicationPreparedAction action1 = new TestApplicationPreparedAction();
|
||||
TestApplicationPreparedAction action2 = new TestApplicationPreparedAction();
|
||||
Registration<Integer> registration = this.registy.register(Integer.class, this.counter::getAndIncrement);
|
||||
registration.onApplicationContextPrepared(action1::run);
|
||||
registration.onApplicationContextPrepared(action2::run);
|
||||
this.registy.applicationContextPrepared(this.context);
|
||||
assertThat(action1).wasCalledOnlyOnce().hasInstanceValue(0);
|
||||
assertThat(action2).wasCalledOnlyOnce().hasInstanceValue(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void registrationGetReturnsInstance() {
|
||||
Registration<Integer> registration = this.registy.register(Integer.class, this.counter::getAndIncrement);
|
||||
assertThat(registration.get()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void registrationGetWhenCalledMultipleTimesReturnsSingleInstance() {
|
||||
Registration<Integer> registration = this.registy.register(Integer.class, this.counter::getAndIncrement);
|
||||
assertThat(registration.get()).isEqualTo(0);
|
||||
assertThat(registration.get()).isEqualTo(0);
|
||||
assertThat(registration.get()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void registrationOnApplicationContextPreparedAddsAction() {
|
||||
TestApplicationPreparedAction action = new TestApplicationPreparedAction();
|
||||
Registration<Integer> registration = this.registy.register(Integer.class, this.counter::getAndIncrement);
|
||||
registration.onApplicationContextPrepared(action::run);
|
||||
this.registy.applicationContextPrepared(this.context);
|
||||
assertThat(action).wasCalledOnlyOnce().hasInstanceValue(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void registrationOnApplicationContextPreparedWhenActionIsNullDoesNotAddAction() {
|
||||
Registration<Integer> registration = this.registy.register(Integer.class, this.counter::getAndIncrement);
|
||||
registration.onApplicationContextPrepared(null);
|
||||
this.registy.applicationContextPrepared(this.context);
|
||||
}
|
||||
|
||||
private static class TestApplicationPreparedAction implements AssertProvider<ApplicationPreparedActionAssert> {
|
||||
|
||||
private Integer instance;
|
||||
|
||||
private int called;
|
||||
|
||||
void run(ConfigurableApplicationContext context, Integer instance) {
|
||||
this.instance = instance;
|
||||
this.called++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApplicationPreparedActionAssert assertThat() {
|
||||
return new ApplicationPreparedActionAssert(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class ApplicationPreparedActionAssert
|
||||
extends AbstractAssert<ApplicationPreparedActionAssert, TestApplicationPreparedAction> {
|
||||
|
||||
ApplicationPreparedActionAssert(TestApplicationPreparedAction actual) {
|
||||
super(actual, ApplicationPreparedActionAssert.class);
|
||||
}
|
||||
|
||||
ApplicationPreparedActionAssert hasInstanceValue(Integer expected) {
|
||||
assertThat(this.actual.instance).isEqualTo(expected);
|
||||
return this;
|
||||
}
|
||||
|
||||
ApplicationPreparedActionAssert wasCalledOnlyOnce() {
|
||||
assertThat(this.actual.called).as("action calls").isEqualTo(1);
|
||||
return this;
|
||||
}
|
||||
|
||||
ApplicationPreparedActionAssert wasNotCalled() {
|
||||
assertThat(this.actual.called).as("action calls").isEqualTo(0);
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -20,6 +20,8 @@ import java.util.List;
|
|||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.BootstrapRegistry;
|
||||
import org.springframework.boot.DefaultBootstrapContext;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
|
||||
import org.springframework.boot.context.event.ApplicationFailedEvent;
|
||||
|
|
@ -45,23 +47,22 @@ class EnvironmentPostProcessorApplicationListenerTests {
|
|||
|
||||
private DeferredLogs deferredLogs = spy(new DeferredLogs());
|
||||
|
||||
private DefaultBootstrapRegisty bootstrapRegistry = spy(new DefaultBootstrapRegisty());
|
||||
private DefaultBootstrapContext bootstrapContext = spy(new DefaultBootstrapContext());
|
||||
|
||||
private EnvironmentPostProcessorApplicationListener listener = new EnvironmentPostProcessorApplicationListener(
|
||||
EnvironmentPostProcessorsFactory.of(TestEnvironmentPostProcessor.class), this.deferredLogs,
|
||||
this.bootstrapRegistry);
|
||||
EnvironmentPostProcessorsFactory.of(TestEnvironmentPostProcessor.class), this.deferredLogs);
|
||||
|
||||
@Test
|
||||
void createUsesSpringFactories() {
|
||||
EnvironmentPostProcessorApplicationListener listener = new EnvironmentPostProcessorApplicationListener();
|
||||
assertThat(listener.getEnvironmentPostProcessors()).hasSizeGreaterThan(1);
|
||||
assertThat(listener.getEnvironmentPostProcessors(this.bootstrapContext)).hasSizeGreaterThan(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void createWhenHasFactoryUsesFactory() {
|
||||
EnvironmentPostProcessorApplicationListener listener = new EnvironmentPostProcessorApplicationListener(
|
||||
EnvironmentPostProcessorsFactory.of(TestEnvironmentPostProcessor.class));
|
||||
List<EnvironmentPostProcessor> postProcessors = listener.getEnvironmentPostProcessors();
|
||||
List<EnvironmentPostProcessor> postProcessors = listener.getEnvironmentPostProcessors(this.bootstrapContext);
|
||||
assertThat(postProcessors).hasSize(1);
|
||||
assertThat(postProcessors.get(0)).isInstanceOf(TestEnvironmentPostProcessor.class);
|
||||
}
|
||||
|
|
@ -90,8 +91,8 @@ class EnvironmentPostProcessorApplicationListenerTests {
|
|||
void onApplicationEventWhenApplicationEnvironmentPreparedEventCallsPostProcessors() {
|
||||
SpringApplication application = mock(SpringApplication.class);
|
||||
MockEnvironment environment = new MockEnvironment();
|
||||
ApplicationEnvironmentPreparedEvent event = new ApplicationEnvironmentPreparedEvent(application, new String[0],
|
||||
environment);
|
||||
ApplicationEnvironmentPreparedEvent event = new ApplicationEnvironmentPreparedEvent(this.bootstrapContext,
|
||||
application, new String[0], environment);
|
||||
this.listener.onApplicationEvent(event);
|
||||
assertThat(environment.getProperty("processed")).isEqualTo("true");
|
||||
}
|
||||
|
|
@ -105,15 +106,6 @@ class EnvironmentPostProcessorApplicationListenerTests {
|
|||
verify(this.deferredLogs).switchOverAll();
|
||||
}
|
||||
|
||||
@Test
|
||||
void onApplicationEventWhenApplicationPreparedEventTriggersRegistryActions() {
|
||||
SpringApplication application = mock(SpringApplication.class);
|
||||
ConfigurableApplicationContext context = mock(ConfigurableApplicationContext.class);
|
||||
ApplicationPreparedEvent event = new ApplicationPreparedEvent(application, new String[0], context);
|
||||
this.listener.onApplicationEvent(event);
|
||||
verify(this.bootstrapRegistry).applicationContextPrepared(context);
|
||||
}
|
||||
|
||||
@Test
|
||||
void onApplicationEventWhenApplicationFailedEventSwitchesLogs() {
|
||||
SpringApplication application = mock(SpringApplication.class);
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import java.util.function.Supplier;
|
|||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.DefaultBootstrapContext;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.logging.DeferredLogFactory;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
|
|
@ -36,13 +37,13 @@ class EnvironmentPostProcessorsFactoryTests {
|
|||
|
||||
private final DeferredLogFactory logFactory = Supplier::get;
|
||||
|
||||
private final BootstrapRegistry bootstrapRegistry = new DefaultBootstrapRegisty();
|
||||
private final DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
|
||||
|
||||
@Test
|
||||
void fromSpringFactoriesReturnsFactory() {
|
||||
EnvironmentPostProcessorsFactory factory = EnvironmentPostProcessorsFactory.fromSpringFactories(null);
|
||||
List<EnvironmentPostProcessor> processors = factory.getEnvironmentPostProcessors(this.logFactory,
|
||||
this.bootstrapRegistry);
|
||||
this.bootstrapContext);
|
||||
assertThat(processors).hasSizeGreaterThan(1);
|
||||
}
|
||||
|
||||
|
|
@ -51,7 +52,7 @@ class EnvironmentPostProcessorsFactoryTests {
|
|||
EnvironmentPostProcessorsFactory factory = EnvironmentPostProcessorsFactory
|
||||
.of(TestEnvironmentPostProcessor.class);
|
||||
List<EnvironmentPostProcessor> processors = factory.getEnvironmentPostProcessors(this.logFactory,
|
||||
this.bootstrapRegistry);
|
||||
this.bootstrapContext);
|
||||
assertThat(processors).hasSize(1);
|
||||
assertThat(processors.get(0)).isInstanceOf(TestEnvironmentPostProcessor.class);
|
||||
}
|
||||
|
|
@ -61,7 +62,7 @@ class EnvironmentPostProcessorsFactoryTests {
|
|||
EnvironmentPostProcessorsFactory factory = EnvironmentPostProcessorsFactory
|
||||
.of(TestEnvironmentPostProcessor.class.getName());
|
||||
List<EnvironmentPostProcessor> processors = factory.getEnvironmentPostProcessors(this.logFactory,
|
||||
this.bootstrapRegistry);
|
||||
this.bootstrapContext);
|
||||
assertThat(processors).hasSize(1);
|
||||
assertThat(processors.get(0)).isInstanceOf(TestEnvironmentPostProcessor.class);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ import java.util.function.Supplier;
|
|||
import org.apache.commons.logging.Log;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.BootstrapRegistry;
|
||||
import org.springframework.boot.DefaultBootstrapContext;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.logging.DeferredLogFactory;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
|
|
@ -40,7 +42,7 @@ class ReflectionEnvironmentPostProcessorsFactoryTests {
|
|||
|
||||
private final DeferredLogFactory logFactory = Supplier::get;
|
||||
|
||||
private final BootstrapRegistry bootstrapRegistry = new DefaultBootstrapRegisty();
|
||||
private final DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
|
||||
|
||||
@Test
|
||||
void createWithClassesCreatesFactory() {
|
||||
|
|
@ -96,7 +98,7 @@ class ReflectionEnvironmentPostProcessorsFactoryTests {
|
|||
ReflectionEnvironmentPostProcessorsFactory factory = new ReflectionEnvironmentPostProcessorsFactory(
|
||||
BadEnvironmentPostProcessor.class.getName());
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> factory.getEnvironmentPostProcessors(this.logFactory, this.bootstrapRegistry))
|
||||
.isThrownBy(() -> factory.getEnvironmentPostProcessors(this.logFactory, this.bootstrapContext))
|
||||
.withMessageContaining("Unable to instantiate");
|
||||
}
|
||||
|
||||
|
|
@ -115,7 +117,7 @@ class ReflectionEnvironmentPostProcessorsFactoryTests {
|
|||
void createsSinglePostProcessor(Class<?> expectedType) {
|
||||
List<EnvironmentPostProcessor> processors = this.factory.getEnvironmentPostProcessors(
|
||||
ReflectionEnvironmentPostProcessorsFactoryTests.this.logFactory,
|
||||
ReflectionEnvironmentPostProcessorsFactoryTests.this.bootstrapRegistry);
|
||||
ReflectionEnvironmentPostProcessorsFactoryTests.this.bootstrapContext);
|
||||
assertThat(processors).hasSize(1);
|
||||
assertThat(processors.get(0)).isInstanceOf(expectedType);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
plugins {
|
||||
id "java"
|
||||
id "org.springframework.boot.conventions"
|
||||
}
|
||||
|
||||
description = "Spring Boot Bootstrap Registry smoke test"
|
||||
|
||||
dependencies {
|
||||
implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter"))
|
||||
|
||||
testImplementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-test"))
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright 2012-2020 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
|
||||
*
|
||||
* https://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 smoketest.bootstrapregistry.app;
|
||||
|
||||
import smoketest.bootstrapregistry.external.svn.SubversionClient;
|
||||
import smoketest.bootstrapregistry.external.svn.SubversionServerCertificate;
|
||||
|
||||
public class MySubversionClient extends SubversionClient {
|
||||
|
||||
public MySubversionClient(SubversionServerCertificate serverCertificate) {
|
||||
super(serverCertificate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String load(String location) {
|
||||
return "my-" + super.load(location);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright 2012-2020 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
|
||||
*
|
||||
* https://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 smoketest.bootstrapregistry.app;
|
||||
|
||||
import smoketest.bootstrapregistry.external.svn.SubversionClient;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class Printer {
|
||||
|
||||
Printer(@Value("${svn}") String svn, SubversionClient subversionClient) {
|
||||
System.out.println("--- svn " + svn);
|
||||
System.out.println("--- client " + subversionClient.getClass().getName());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright 2012-2020 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
|
||||
*
|
||||
* https://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 smoketest.bootstrapregistry.app;
|
||||
|
||||
import smoketest.bootstrapregistry.external.svn.SubversionBootstrap;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class SampleBootstrapRegistryApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
// This example shows how a Bootstrapper can be used to register a custom
|
||||
// SubversionClient that still has access to data provided in the
|
||||
// application.properties file
|
||||
SpringApplication application = new SpringApplication(SampleBootstrapRegistryApplication.class);
|
||||
application.addBootstrapper(SubversionBootstrap.withCustomClient(MySubversionClient::new));
|
||||
application.run(args);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright 2012-2020 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
|
||||
*
|
||||
* https://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 smoketest.bootstrapregistry.external.svn;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.boot.BootstrapContext;
|
||||
import org.springframework.boot.Bootstrapper;
|
||||
|
||||
/**
|
||||
* Allows the user to register a {@link Bootstrapper} with a custom
|
||||
* {@link SubversionClient}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public final class SubversionBootstrap {
|
||||
|
||||
private SubversionBootstrap() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link Bootstrapper} for the given client factory.
|
||||
* @param clientFactory the client factory
|
||||
* @return a {@link Bootstrapper} instance
|
||||
*/
|
||||
public static Bootstrapper withCustomClient(Function<SubversionServerCertificate, SubversionClient> clientFactory) {
|
||||
return (registry) -> registry.register(SubversionClient.class,
|
||||
(bootstrapContext) -> createSubversionClient(bootstrapContext, clientFactory));
|
||||
}
|
||||
|
||||
private static SubversionClient createSubversionClient(BootstrapContext bootstrapContext,
|
||||
Function<SubversionServerCertificate, SubversionClient> clientFactory) {
|
||||
return clientFactory.apply(bootstrapContext.get(SubversionServerCertificate.class));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright 2012-2020 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
|
||||
*
|
||||
* https://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 smoketest.bootstrapregistry.external.svn;
|
||||
|
||||
/**
|
||||
* A client that can connect to a subversion server.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class SubversionClient {
|
||||
|
||||
private SubversionServerCertificate serverCertificate;
|
||||
|
||||
public SubversionClient(SubversionServerCertificate serverCertificate) {
|
||||
this.serverCertificate = serverCertificate;
|
||||
}
|
||||
|
||||
public String load(String location) {
|
||||
return "data from svn / " + location + "[" + this.serverCertificate + "]";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright 2012-2020 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
|
||||
*
|
||||
* https://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 smoketest.bootstrapregistry.external.svn;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.springframework.boot.BootstrapContext;
|
||||
import org.springframework.boot.BootstrapContextClosedEvent;
|
||||
import org.springframework.boot.BootstrapRegistry;
|
||||
import org.springframework.boot.BootstrapRegistry.InstanceSupplier;
|
||||
import org.springframework.boot.context.config.ConfigData;
|
||||
import org.springframework.boot.context.config.ConfigDataLoader;
|
||||
import org.springframework.boot.context.config.ConfigDataLoaderContext;
|
||||
import org.springframework.boot.context.config.ConfigDataLocationNotFoundException;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
|
||||
/**
|
||||
* {@link ConfigDataLoader} for subversion.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class SubversionConfigDataLoader implements ConfigDataLoader<SubversionConfigDataLocation> {
|
||||
|
||||
private static final ApplicationListener<BootstrapContextClosedEvent> closeListener = SubversionConfigDataLoader::onBootstrapContextClosed;
|
||||
|
||||
SubversionConfigDataLoader(BootstrapRegistry bootstrapRegistry) {
|
||||
bootstrapRegistry.registerIfAbsent(SubversionClient.class, this::createSubversionClient);
|
||||
bootstrapRegistry.addCloseListener(closeListener);
|
||||
}
|
||||
|
||||
private SubversionClient createSubversionClient(BootstrapContext bootstrapContext) {
|
||||
return new SubversionClient(bootstrapContext.get(SubversionServerCertificate.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigData load(ConfigDataLoaderContext context, SubversionConfigDataLocation location)
|
||||
throws IOException, ConfigDataLocationNotFoundException {
|
||||
context.getBootstrapContext().registerIfAbsent(SubversionServerCertificate.class,
|
||||
InstanceSupplier.of(location.getServerCertificate()));
|
||||
SubversionClient client = context.getBootstrapContext().get(SubversionClient.class);
|
||||
String loaded = client.load(location.getLocation());
|
||||
PropertySource<?> propertySource = new MapPropertySource("svn", Collections.singletonMap("svn", loaded));
|
||||
return new ConfigData(Collections.singleton(propertySource));
|
||||
}
|
||||
|
||||
private static void onBootstrapContextClosed(BootstrapContextClosedEvent event) {
|
||||
event.getApplicationContext().getBeanFactory().registerSingleton("subversionClient",
|
||||
event.getBootstrapContext().get(SubversionClient.class));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright 2012-2020 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
|
||||
*
|
||||
* https://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 smoketest.bootstrapregistry.external.svn;
|
||||
|
||||
import org.springframework.boot.context.config.ConfigDataLocation;
|
||||
|
||||
/**
|
||||
* A subversion {@link ConfigDataLocation}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class SubversionConfigDataLocation extends ConfigDataLocation {
|
||||
|
||||
private final String location;
|
||||
|
||||
private final SubversionServerCertificate serverCertificate;
|
||||
|
||||
SubversionConfigDataLocation(String location, String serverCertificate) {
|
||||
this.location = location;
|
||||
this.serverCertificate = SubversionServerCertificate.of(serverCertificate);
|
||||
}
|
||||
|
||||
String getLocation() {
|
||||
return this.location;
|
||||
}
|
||||
|
||||
SubversionServerCertificate getServerCertificate() {
|
||||
return this.serverCertificate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
SubversionConfigDataLocation other = (SubversionConfigDataLocation) obj;
|
||||
return this.location.equals(other.location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.location.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.location;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright 2012-2020 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
|
||||
*
|
||||
* https://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 smoketest.bootstrapregistry.external.svn;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.boot.context.config.ConfigDataLocationNotFoundException;
|
||||
import org.springframework.boot.context.config.ConfigDataLocationResolver;
|
||||
import org.springframework.boot.context.config.ConfigDataLocationResolverContext;
|
||||
|
||||
/**
|
||||
* {@link ConfigDataLocationResolver} for subversion.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class SubversionConfigDataLocationResolver implements ConfigDataLocationResolver<SubversionConfigDataLocation> {
|
||||
|
||||
private static final String PREFIX = "svn:";
|
||||
|
||||
@Override
|
||||
public boolean isResolvable(ConfigDataLocationResolverContext context, String location) {
|
||||
return location.startsWith(PREFIX);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SubversionConfigDataLocation> resolve(ConfigDataLocationResolverContext context, String location,
|
||||
boolean optional) throws ConfigDataLocationNotFoundException {
|
||||
String serverCertificate = context.getBinder().bind("spring.svn.server.certificate", String.class).orElse(null);
|
||||
return Collections.singletonList(
|
||||
new SubversionConfigDataLocation(location.substring(PREFIX.length()), serverCertificate));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright 2012-2020 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
|
||||
*
|
||||
* https://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 smoketest.bootstrapregistry.external.svn;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A certificate that can be used to provide a secure connection to the subversion server.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class SubversionServerCertificate {
|
||||
|
||||
private final String data;
|
||||
|
||||
SubversionServerCertificate(String data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.data;
|
||||
}
|
||||
|
||||
public static SubversionServerCertificate of(String data) {
|
||||
return StringUtils.hasText(data) ? new SubversionServerCertificate(data) : null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright 2012-2020 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* An example of a hypothetical library that supports subversion.
|
||||
*/
|
||||
package smoketest.bootstrapregistry.external.svn;
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
org.springframework.boot.context.config.ConfigDataLocationResolver=\
|
||||
smoketest.bootstrapregistry.external.svn.SubversionConfigDataLocationResolver
|
||||
|
||||
org.springframework.boot.context.config.ConfigDataLoader=\
|
||||
smoketest.bootstrapregistry.external.svn.SubversionConfigDataLoader
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
spring.svn.server.certificate=secret
|
||||
spring.config.import=svn:example.com
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright 2012-2020 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
|
||||
*
|
||||
* https://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 smoketest.bootstrapregistry.app;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
import org.springframework.boot.test.system.CapturedOutput;
|
||||
import org.springframework.boot.test.system.OutputCaptureExtension;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link SampleBootstrapRegistryApplication}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@ExtendWith(OutputCaptureExtension.class)
|
||||
class SampleBootstrapRegistryApplicationTests {
|
||||
|
||||
@Test
|
||||
void testBootrapper(CapturedOutput output) {
|
||||
SampleBootstrapRegistryApplication.main(new String[0]);
|
||||
assertThat(output).contains("svn my-data from svn / example.com[secret]")
|
||||
.contains("client smoketest.bootstrapregistry.app.MySubversionClient");
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue