From 2deb202c210479e4d022620f3ec25856cbe7bbdb Mon Sep 17 00:00:00 2001 From: John Blum Date: Thu, 23 Apr 2020 14:47:04 -0700 Subject: [PATCH] Add new AbstractContextLoader implementation and extension to the Spring TestContext Framework to configure and bootstrap the RefreshableAnnotationConfigApplicationContext used in Spring based tests. --- ...stRefreshableApplicationContextLoader.java | 151 ++++++++++++++++++ ...licationContextLoaderIntegrationTests.java | 52 ++++++ 2 files changed, 203 insertions(+) create mode 100644 spring-geode/src/main/java/org/springframework/geode/test/context/TestRefreshableApplicationContextLoader.java create mode 100644 spring-geode/src/test/java/org/springframework/geode/test/context/TestRefreshableApplicationContextLoaderIntegrationTests.java diff --git a/spring-geode/src/main/java/org/springframework/geode/test/context/TestRefreshableApplicationContextLoader.java b/spring-geode/src/main/java/org/springframework/geode/test/context/TestRefreshableApplicationContextLoader.java new file mode 100644 index 00000000..8a635e07 --- /dev/null +++ b/spring-geode/src/main/java/org/springframework/geode/test/context/TestRefreshableApplicationContextLoader.java @@ -0,0 +1,151 @@ +/* + * Copyright 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.geode.test.context; + +import java.util.Optional; + +import org.springframework.context.ApplicationContext; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.AnnotationConfigRegistry; +import org.springframework.data.gemfire.util.ArrayUtils; +import org.springframework.geode.context.annotation.RefreshableAnnotationConfigApplicationContext; +import org.springframework.test.context.ContextConfigurationAttributes; +import org.springframework.test.context.MergedContextConfiguration; +import org.springframework.test.context.TestContext; +import org.springframework.test.context.support.AbstractContextLoader; + +/** + * An {@link AbstractContextLoader} from the Spring {@link TestContext} Framework used to load + * a {@link RefreshableAnnotationConfigApplicationContext}. + * + * @author John Blum + * @see org.springframework.context.ApplicationContext + * @see org.springframework.context.ConfigurableApplicationContext + * @see org.springframework.context.annotation.AnnotationConfigRegistry + * @see org.springframework.geode.context.annotation.RefreshableAnnotationConfigApplicationContext + * @see org.springframework.test.context.ContextConfigurationAttributes + * @see org.springframework.test.context.MergedContextConfiguration + * @see org.springframework.test.context.TestContext + * @see org.springframework.test.context.support.AbstractContextLoader + * @since 1.3.0 + */ +@SuppressWarnings("unused") +public class TestRefreshableApplicationContextLoader extends AbstractContextLoader { + + protected static final String DEFAULT_RESOURCE_SUFFIX = "-context"; + + private Class testClass; + + /** + * @inheritDoc + */ + @Override + public void processContextConfiguration(ContextConfigurationAttributes configAttributes) { + + super.processContextConfiguration(configAttributes); + + this.testClass = configAttributes.getDeclaringClass(); + } + + /** + * @inheritDoc + */ + @Override + public ApplicationContext loadContext(MergedContextConfiguration mergedConfig) { + + ConfigurableApplicationContext applicationContext = + configure(newApplicationContext(mergedConfig), mergedConfig); + + applicationContext.registerShutdownHook(); + applicationContext.refresh(); + + return applicationContext; + } + + /** + * Constructs a new instance of the {@link RefreshableAnnotationConfigApplicationContext} initialized from + * the given {{@link TestContext} @link MergedContextConfiguration merged configuration meta-data}. + * + * @param contextConfiguration {@link MergedContextConfiguration} from the {@link TestContext} used to configure + * and bootstrap a new {@link RefreshableAnnotationConfigApplicationContext}. + * @return a configured and bootstrapped {@link ConfigurableApplicationContext} implementation. + * @see org.springframework.context.ConfigurableApplicationContext + * @see org.springframework.geode.context.annotation.RefreshableAnnotationConfigApplicationContext + * @see org.springframework.test.context.MergedContextConfiguration + * @see #prepareContext(ConfigurableApplicationContext, MergedContextConfiguration) + */ + protected ConfigurableApplicationContext newApplicationContext(MergedContextConfiguration contextConfiguration) { + + RefreshableAnnotationConfigApplicationContext applicationContext = + new RefreshableAnnotationConfigApplicationContext(contextConfiguration.getParentApplicationContext()); + + prepareContext(applicationContext, contextConfiguration); + + return applicationContext; + } + + private ConfigurableApplicationContext configure(ConfigurableApplicationContext applicationContext, + MergedContextConfiguration contextConfiguration) { + + applicationContext = configureComponentClasses(applicationContext, contextConfiguration); + applicationContext = configureScan(applicationContext, contextConfiguration); + + customizeContext(applicationContext, contextConfiguration); + + return applicationContext; + } + + private ConfigurableApplicationContext configureComponentClasses( + ConfigurableApplicationContext applicationContext, MergedContextConfiguration contextConfiguration) { + + Optional.ofNullable(applicationContext) + .filter(it -> ArrayUtils.isNotEmpty(contextConfiguration.getClasses())) + .filter(AnnotationConfigRegistry.class::isInstance) + .map(AnnotationConfigRegistry.class::cast) + .ifPresent(registry -> registry.register(contextConfiguration.getClasses())); + + return applicationContext; + } + + private ConfigurableApplicationContext configureScan(ConfigurableApplicationContext applicationContext, + MergedContextConfiguration contextConfiguration) { + + Optional.ofNullable(applicationContext) + .filter(it -> ArrayUtils.isNotEmpty(contextConfiguration.getLocations())) + .filter(AnnotationConfigRegistry.class::isInstance) + .map(AnnotationConfigRegistry.class::cast) + .ifPresent(registry -> registry.scan(contextConfiguration.getLocations())); + + return applicationContext; + } + + /** + * @inheritDoc + */ + @Override + public ApplicationContext loadContext(String... locations) { + return loadContext(new MergedContextConfiguration(this.testClass, locations, new Class[0], new String[0], + this)); + } + + /** + * @inheritDoc + */ + @Override + protected String getResourceSuffix() { + return DEFAULT_RESOURCE_SUFFIX; + } +} diff --git a/spring-geode/src/test/java/org/springframework/geode/test/context/TestRefreshableApplicationContextLoaderIntegrationTests.java b/spring-geode/src/test/java/org/springframework/geode/test/context/TestRefreshableApplicationContextLoaderIntegrationTests.java new file mode 100644 index 00000000..5436ded9 --- /dev/null +++ b/spring-geode/src/test/java/org/springframework/geode/test/context/TestRefreshableApplicationContextLoaderIntegrationTests.java @@ -0,0 +1,52 @@ +/* + * Copyright 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.geode.test.context; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.geode.context.annotation.RefreshableAnnotationConfigApplicationContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; + +/** + * Integration Tests for {@link TestRefreshableApplicationContextLoader}. + * + * @author John Blum + * @see org.junit.Test + * @see org.springframework.geode.context.annotation.RefreshableAnnotationConfigApplicationContext + * @see org.springframework.geode.test.context.TestRefreshableApplicationContextLoader + * @see org.springframework.test.context.ContextConfiguration + * @see org.springframework.test.context.junit4.SpringRunner + * @since 1.3.0 + */ +@RunWith(SpringRunner.class) +@ContextConfiguration(loader = TestRefreshableApplicationContextLoader.class) +@SuppressWarnings("unused") +public class TestRefreshableApplicationContextLoaderIntegrationTests { + + @Autowired + private ApplicationContext applicationContext; + + @Test + public void applicationContextIsAnRefreshableAnnotationConfigApplicationContext() { + assertThat(this.applicationContext).isInstanceOf(RefreshableAnnotationConfigApplicationContext.class); + } +}