From ebfaff614db14c192d3ddc82d83bc8e1ffbee069 Mon Sep 17 00:00:00 2001 From: John Blum Date: Wed, 8 Aug 2018 19:16:58 -0700 Subject: [PATCH] Add property-based configuration option when enabling Spring Session and configuring either Apache Geode or Pivotal GemFire as an HTTP Session state management provider. --- ...pSessionConfigurationIntegrationTests.java | 172 ++++++++++ ...actGemFireOperationsSessionRepository.java | 5 +- ...stractGemFireHttpSessionConfiguration.java | 314 ++++++++++++++++++ .../http/GemFireHttpSessionConfiguration.java | 127 ++----- 4 files changed, 525 insertions(+), 93 deletions(-) create mode 100644 spring-session-data-geode/src/integration-test/java/org/springframework/session/data/gemfire/config/annotation/web/http/PropertyBasedGemFireHttpSessionConfigurationIntegrationTests.java create mode 100644 spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/config/annotation/web/http/AbstractGemFireHttpSessionConfiguration.java diff --git a/spring-session-data-geode/src/integration-test/java/org/springframework/session/data/gemfire/config/annotation/web/http/PropertyBasedGemFireHttpSessionConfigurationIntegrationTests.java b/spring-session-data-geode/src/integration-test/java/org/springframework/session/data/gemfire/config/annotation/web/http/PropertyBasedGemFireHttpSessionConfigurationIntegrationTests.java new file mode 100644 index 0000000..ff3cd2b --- /dev/null +++ b/spring-session-data-geode/src/integration-test/java/org/springframework/session/data/gemfire/config/annotation/web/http/PropertyBasedGemFireHttpSessionConfigurationIntegrationTests.java @@ -0,0 +1,172 @@ +/* + * Copyright 2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.session.data.gemfire.config.annotation.web.http; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +import java.util.Optional; + +import org.junit.After; +import org.junit.Test; + +import org.apache.geode.cache.RegionShortcut; +import org.apache.geode.cache.client.ClientRegionShortcut; + +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.core.env.PropertySource; +import org.springframework.data.gemfire.config.annotation.ClientCacheApplication; +import org.springframework.data.gemfire.tests.mock.annotation.EnableGemFireMockObjects; +import org.springframework.mock.env.MockPropertySource; +import org.springframework.session.Session; +import org.springframework.session.data.gemfire.serialization.SessionSerializer; + +/** + * Integration tests testing property-based configuration of either Apache Geode or Pivotal GemFire + * as the (HTTP) {@link Session} state management provider in Spring Session. + * + * @author John Blum + * @see org.junit.Test + * @see org.springframework.context.ConfigurableApplicationContext + * @see org.springframework.context.annotation.AnnotationConfigApplicationContext + * @see org.springframework.core.env.PropertySource + * @see org.springframework.data.gemfire.config.annotation.ClientCacheApplication + * @see org.springframework.data.gemfire.tests.mock.annotation.EnableGemFireMockObjects + * @see org.springframework.mock.env.MockPropertySource + * @since 2.0.4 + */ +@SuppressWarnings("unused") +public class PropertyBasedGemFireHttpSessionConfigurationIntegrationTests { + + private ConfigurableApplicationContext applicationContext; + + @After + public void tearDow() { + Optional.ofNullable(this.applicationContext).ifPresent(ConfigurableApplicationContext::close); + } + + private ConfigurableApplicationContext newApplicationContext(Class... annotatedClasses) { + return newApplicationContext(new MockPropertySource("TestProperties"), annotatedClasses); + } + + private ConfigurableApplicationContext newApplicationContext(PropertySource testPropertySource, + Class... annotatedClasses) { + + AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); + + applicationContext.getEnvironment().getPropertySources().addFirst(testPropertySource); + applicationContext.register(annotatedClasses); + applicationContext.registerShutdownHook(); + applicationContext.refresh(); + + return applicationContext; + } + + @Test + public void usesAnnotationAttributeConfigurationExclusively() { + + this.applicationContext = newApplicationContext(TestConfiguration.class); + + GemFireHttpSessionConfiguration sessionConfiguration = + this.applicationContext.getBean(GemFireHttpSessionConfiguration.class); + + assertThat(sessionConfiguration).isNotNull(); + assertThat(sessionConfiguration.getClientRegionShortcut()).isEqualTo(ClientRegionShortcut.LOCAL); + assertThat(sessionConfiguration.getIndexableSessionAttributes()).containsExactly("one", "two"); + assertThat(sessionConfiguration.getMaxInactiveIntervalInSeconds()).isEqualTo(900); + assertThat(sessionConfiguration.getPoolName()).isEqualTo("Swimming"); + assertThat(sessionConfiguration.getServerRegionShortcut()).isEqualTo(RegionShortcut.LOCAL); + assertThat(sessionConfiguration.getSessionRegionName()).isEqualTo("AnnotationAttributeRegionName"); + assertThat(sessionConfiguration.getSessionSerializerBeanName()).isEqualTo("TestSessionSerializer"); + } + + @Test + public void usesAnnotationAttributesAndPropertyBasedConfiguration() { + + MockPropertySource testPropertySource = new MockPropertySource("TestProperties") + .withProperty("spring.session.data.gemfire.cache.client.pool.name", "Dead") + .withProperty("spring.session.data.gemfire.session.attributes.indexable", "two, four") + .withProperty("spring.session.data.gemfire.session.region.name", "TestRegionName"); + + this.applicationContext = newApplicationContext(testPropertySource, TestConfiguration.class); + + GemFireHttpSessionConfiguration sessionConfiguration = + this.applicationContext.getBean(GemFireHttpSessionConfiguration.class); + + assertThat(sessionConfiguration).isNotNull(); + assertThat(sessionConfiguration.getClientRegionShortcut()).isEqualTo(ClientRegionShortcut.LOCAL); + assertThat(sessionConfiguration.getIndexableSessionAttributes()).containsExactly("two", "four"); + assertThat(sessionConfiguration.getMaxInactiveIntervalInSeconds()).isEqualTo(900); + assertThat(sessionConfiguration.getPoolName()).isEqualTo("Dead"); + assertThat(sessionConfiguration.getServerRegionShortcut()).isEqualTo(RegionShortcut.LOCAL); + assertThat(sessionConfiguration.getSessionRegionName()).isEqualTo("TestRegionName"); + assertThat(sessionConfiguration.getSessionSerializerBeanName()).isEqualTo("TestSessionSerializer"); + } + + @Test + public void usesPropertyBasedConfigurationExclusively() { + + MockPropertySource testPropertySource = new MockPropertySource("TestProperties") + .withProperty("spring.session.data.gemfire.cache.client.pool.name", "Dead") + .withProperty("spring.session.data.gemfire.cache.client.region.shortcut", ClientRegionShortcut.CACHING_PROXY.name()) + .withProperty("spring.session.data.gemfire.cache.server.region.shortcut", RegionShortcut.REPLICATE_PERSISTENT_OVERFLOW.name()) + .withProperty("spring.session.data.gemfire.session.attributes.indexable", "firstName, lastName") + .withProperty("spring.session.data.gemfire.session.expiration.max-inactive-interval-seconds", "3600") + .withProperty("spring.session.data.gemfire.session.region.name", "PropertyRegionName") + .withProperty("spring.session.data.gemfire.session.serializer.bean-name", "MockSessionSerializer"); + + this.applicationContext = newApplicationContext(testPropertySource, TestConfiguration.class); + + GemFireHttpSessionConfiguration sessionConfiguration = + this.applicationContext.getBean(GemFireHttpSessionConfiguration.class); + + assertThat(sessionConfiguration).isNotNull(); + assertThat(sessionConfiguration.getClientRegionShortcut()).isEqualTo(ClientRegionShortcut.CACHING_PROXY); + assertThat(sessionConfiguration.getIndexableSessionAttributes()).containsExactly("firstName", "lastName"); + assertThat(sessionConfiguration.getMaxInactiveIntervalInSeconds()).isEqualTo(3600); + assertThat(sessionConfiguration.getPoolName()).isEqualTo("Dead"); + assertThat(sessionConfiguration.getServerRegionShortcut()).isEqualTo(RegionShortcut.REPLICATE_PERSISTENT_OVERFLOW); + assertThat(sessionConfiguration.getSessionRegionName()).isEqualTo("PropertyRegionName"); + assertThat(sessionConfiguration.getSessionSerializerBeanName()).isEqualTo("MockSessionSerializer"); + } + + @ClientCacheApplication + @EnableGemFireMockObjects + @EnableGemFireHttpSession( + clientRegionShortcut = ClientRegionShortcut.LOCAL, + indexableSessionAttributes = { "one", "two" }, + maxInactiveIntervalInSeconds = 900, + poolName = "Swimming", + regionName = "AnnotationAttributeRegionName", + serverRegionShortcut = RegionShortcut.LOCAL, + sessionSerializerBeanName = "TestSessionSerializer" + ) + static class TestConfiguration { + + @Bean("MockSessionSerializer") + Object mockSessionSerializer() { + return mock(SessionSerializer.class); + } + + @Bean("TestSessionSerializer") + Object testSessionSerializer() { + return mock(SessionSerializer.class); + } + } +} diff --git a/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/AbstractGemFireOperationsSessionRepository.java b/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/AbstractGemFireOperationsSessionRepository.java index c4cdf40..efb82dd 100644 --- a/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/AbstractGemFireOperationsSessionRepository.java +++ b/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/AbstractGemFireOperationsSessionRepository.java @@ -42,6 +42,7 @@ import org.apache.geode.DataSerializer; import org.apache.geode.Delta; import org.apache.geode.Instantiator; import org.apache.geode.InvalidDeltaException; +import org.apache.geode.cache.AttributesMutator; import org.apache.geode.cache.EntryEvent; import org.apache.geode.cache.Operation; import org.apache.geode.cache.Region; @@ -296,7 +297,9 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi this.fullyQualifiedRegionName = region.getFullPath(); - region.getAttributesMutator().addCacheListener(this); + AttributesMutator attributesMutator = region.getAttributesMutator(); + + attributesMutator.addCacheListener(this); } boolean isCreate(EntryEvent event) { diff --git a/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/config/annotation/web/http/AbstractGemFireHttpSessionConfiguration.java b/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/config/annotation/web/http/AbstractGemFireHttpSessionConfiguration.java new file mode 100644 index 0000000..35244e6 --- /dev/null +++ b/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/config/annotation/web/http/AbstractGemFireHttpSessionConfiguration.java @@ -0,0 +1,314 @@ +/* + * Copyright 2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.session.data.gemfire.config.annotation.web.http; + +import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalStateException; + +import java.util.Objects; +import java.util.Optional; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanClassLoaderAware; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.EnvironmentAware; +import org.springframework.core.env.Environment; +import org.springframework.session.config.annotation.web.http.SpringHttpSessionConfiguration; +import org.springframework.util.ObjectUtils; +import org.springframework.util.StringUtils; + +/** + * The {@link AbstractGemFireHttpSessionConfiguration} class is an abstract base class containing configuration logic + * common to Apache Geode and Pivotal GemFire in order to manage {@link javax.servlet.http.HttpSession} state. + * + * @author John Blum + * @see java.lang.ClassLoader + * @see org.springframework.beans.factory.BeanClassLoaderAware + * @see org.springframework.beans.factory.config.ConfigurableBeanFactory + * @see org.springframework.core.env.Environment + * @see org.springframework.context.ApplicationContext + * @see org.springframework.context.ConfigurableApplicationContext + * @see org.springframework.context.EnvironmentAware + * @see org.springframework.session.config.annotation.web.http.SpringHttpSessionConfiguration + * @since 2.0.4 + */ +@SuppressWarnings("unused") +public abstract class AbstractGemFireHttpSessionConfiguration extends SpringHttpSessionConfiguration + implements BeanClassLoaderAware, EnvironmentAware { + + protected static final String SPRING_SESSION_PROPERTY_PREFIX = "spring.session.data.gemfire."; + + private ApplicationContext applicationContext; + + private ClassLoader beanClassLoader; + + private Environment environment; + + /** + * Sets a reference the Spring {@link ApplicationContext}. + * + * @param applicationContext reference to the Spring {@link ApplicationContext}. + * @throws BeansException if the reference cannot be stored. + * @see org.springframework.context.ApplicationContext + */ + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + super.setApplicationContext(applicationContext); + this.applicationContext = applicationContext; + } + + /** + * Returns a reference to the Spring {@link ApplicationContext}. + * + * @return a reference to the Spring {@link ApplicationContext}. + * @see org.springframework.context.ApplicationContext + */ + protected ApplicationContext getApplicationContext() { + return Optional.ofNullable(this.applicationContext) + .orElseThrow(() -> newIllegalStateException("The ApplicationContext was not properly configured")); + } + + /** + * Sets a reference to the {@link ClassLoader} used by the Spring container to load bean {@link Class class types}. + * + * @param beanClassLoader {@link ClassLoader} used by the Spring container to load bean {@link Class class types}. + * @see org.springframework.beans.factory.BeanClassLoaderAware#setBeanClassLoader(ClassLoader) + * @see java.lang.ClassLoader + */ + public void setBeanClassLoader(ClassLoader beanClassLoader) { + this.beanClassLoader = beanClassLoader; + } + + /** + * Returns a reference to the {@link ClassLoader} used by the Spring container to load bean + * {@link Class class types}. + * + * @return the {@link ClassLoader} used by the Spring container to load bean {@link Class class types}. + * @see java.lang.ClassLoader + */ + protected ClassLoader getBeanClassLoader() { + return this.beanClassLoader; + } + + /** + * Returns a reference to the Spring container {@link ConfigurableBeanFactory}. + * + * @return a reference to the Spring container {@link ConfigurableBeanFactory}. + * @see org.springframework.beans.factory.config.ConfigurableBeanFactory + * @see #getApplicationContext() + */ + protected ConfigurableBeanFactory getBeanFactory() { + + ApplicationContext applicationContext = getApplicationContext(); + + return Optional.ofNullable(applicationContext) + .filter(it -> it instanceof ConfigurableApplicationContext) + .map(it -> ((ConfigurableApplicationContext) it).getBeanFactory()) + .orElseThrow(() -> newIllegalStateException("Unable to resolve a reference to a [%1$s] from a [%2$s]", + ConfigurableBeanFactory.class.getName(), ObjectUtils.nullSafeClassName(applicationContext))); + } + + /** + * Sets a reference to the Spring {@link Environment}. + * + * @param environment Spring {@link Environment}. + * @see org.springframework.core.env.Environment + */ + @Override + public void setEnvironment(Environment environment) { + this.environment = environment; + } + + /** + * Returns a reference to the configured Spring {@link Environment}. + * + * @return a reference to the configured Spring {@link Environment}. + * @see org.springframework.core.env.Environment + */ + protected Environment getEnvironment() { + return this.environment; + } + + protected String clientRegionShortcutPropertyName() { + return propertyName("cache.client.region.shortcut"); + } + + protected String indexableSessionAttributesPropertyName() { + return sessionPropertyName("attributes.indexable"); + } + + protected String maxInactiveIntervalInSecondsPropertyName() { + return sessionPropertyName("expiration.max-inactive-interval-seconds"); + } + + protected String poolNamePropertyName() { + return propertyName("cache.client.pool.name"); + } + + protected String serverRegionShortcutPropertyName() { + return propertyName("cache.server.region.shortcut"); + } + + protected String sessionPropertyName(String propertyNameSuffix) { + return propertyName(String.format("session.%s", propertyNameSuffix)); + } + + protected String sessionRegionNamePropertyName() { + return sessionPropertyName("region.name"); + } + + protected String sessionSerializerBeanNamePropertyName() { + return sessionPropertyName("serializer.bean-name"); + } + + /** + * Returns the fully-qualified {@link String property name}. + * + * The fully qualified {@link String property name} consists of the {@link String base property name} + * concatenated with the {@code propertyNameSuffix}. + * + * @param propertyNameSuffix {@link String} containing the property name suffix concatenated with + * the {@link String base property name}. + * @return the fully-qualified {@link String property name}. + * @see java.lang.String + */ + protected String propertyName(String propertyNameSuffix) { + return String.format("%1$s%2$s", SPRING_SESSION_PROPERTY_PREFIX, propertyNameSuffix); + } + + /** + * Resolves the value for the given property identified by {@link String name} from the Spring {@link Environment} + * as an instance of the specified {@link Class type}. + * + * @param {@link Class} type of the {@code propertyName property's} assigned value. + * @param propertyName {@link String} containing the name of the required property to resolve. + * @param type {@link Class} type of the property's assigned value. + * @return the assigned value of the {@link String named} property. + * @throws IllegalStateException if the property has not been assigned a value. + * For {@link String} values, this also means the value cannot be {@link String#isEmpty() empty}. + * For non-{@link String} values, this means the value must not be {@literal null}. + * @see #resolveProperty(String, Class, Object) + */ + protected T requireProperty(String propertyName, Class type) { + + return Optional.of(propertyName) + .map(it -> resolveProperty(propertyName, type, null)) + .filter(Objects::nonNull) + .filter(value -> !(value instanceof String) || StringUtils.hasText((String) value)) + .orElseThrow(() -> newIllegalStateException("Property [%s] is required", propertyName)); + } + + /** + * Attempts to resolve the property with the given {@link String name} from the Spring {@link Environment} + * as an {@link Enum}. + * + * @param propertyName {@link String name} of the property to resolve. + * @param defaultValue default value to return if the property is not defined or not set. + * @return the value of the property identified by {@link String name} or default value if the property + * is not defined or not set. + * @see #resolveProperty(String, Class, Object) + * @see java.lang.Enum + */ + protected > T resolveEnumeratedProperty(String propertyName, Class targetType, T defaultValue) { + + return resolveProperty(propertyName, targetType, defaultValue); + } + + /** + * Attempts to resolve the property with the given {@link String name} from the Spring {@link Environment} + * as an {@link Integer}. + * + * @param propertyName {@link String name} of the property to resolve. + * @param defaultValue default value to return if the property is not defined or not set. + * @return the value of the property identified by {@link String name} or default value if the property + * is not defined or not set. + * @see #resolveProperty(String, Class, Object) + * @see java.lang.Integer + */ + protected Integer resolveProperty(String propertyName, Integer defaultValue) { + return resolveProperty(propertyName, Integer.class, defaultValue); + } + + /** + * Attempts to resolve the property with the given {@link String name} from the Spring {@link Environment} + * as a {@link String}. + * + * @param propertyName {@link String name} of the property to resolve. + * @param defaultValue default value to return if the property is not defined or not set. + * @return the value of the property identified by {@link String name} or default value if the property + * is not defined or not set. + * @see #resolveProperty(String, Class, Object) + * @see java.lang.String + */ + protected String resolveProperty(String propertyName, String defaultValue) { + return resolveProperty(propertyName, String.class, defaultValue); + } + + /** + * Attempts to resolve the property with the given {@link String name} from the Spring {@link Environment} + * as a {@link String} array. + * + * @param propertyName {@link String name} of the property to resolve. + * @param defaultValue default value to return if the property is not defined or not set. + * @return the value of the property identified by {@link String name} or default value if the property + * is not defined or not set. + * @see #resolveProperty(String, Class, Object) + * @see java.lang.String + */ + protected String[] resolveProperty(String propertyName, String[] defaultValue) { + return resolveProperty(propertyName, String[].class, defaultValue); + } + + /** + * Attempts to resolve the property with the given {@link String name} from the Spring {@link Environment}. + * + * @param {@link Class} type of the property value. + * @param propertyName {@link String name} of the property to resolve. + * @param targetType {@link Class} type of the property's value. + * @return the value of the property identified by {@link String name} or {@literal null} if the property + * is not defined or not set. + * @see #resolveProperty(String, Class, Object) + */ + protected T resolveProperty(String propertyName, Class targetType) { + return resolveProperty(propertyName, targetType, null); + } + + /** + * Attempts to resolve the property with the given {@link String name} from the Spring {@link Environment}. + * + * @param {@link Class} type of the property value. + * @param propertyName {@link String name} of the property to resolve. + * @param targetType {@link Class} type of the property's value. + * @param defaultValue default value to return if the property is not defined or not set. + * @return the value of the property identified by {@link String name} or default value if the property + * is not defined or not set. + * @see #getEnvironment() + */ + protected T resolveProperty(String propertyName, Class targetType, T defaultValue) { + + return Optional.ofNullable(getEnvironment()) + .filter(environment -> environment.containsProperty(propertyName)) + .map(environment -> { + + String resolvedPropertyName = environment.resolveRequiredPlaceholders(propertyName); + + return environment.getProperty(resolvedPropertyName, targetType, defaultValue); + }) + .orElse(defaultValue); + } +} diff --git a/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/config/annotation/web/http/GemFireHttpSessionConfiguration.java b/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/config/annotation/web/http/GemFireHttpSessionConfiguration.java index 3fd9049..0a51259 100644 --- a/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/config/annotation/web/http/GemFireHttpSessionConfiguration.java +++ b/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/config/annotation/web/http/GemFireHttpSessionConfiguration.java @@ -16,8 +16,6 @@ package org.springframework.session.data.gemfire.config.annotation.web.http; -import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalStateException; - import java.util.Optional; import java.util.concurrent.TimeUnit; @@ -37,12 +35,8 @@ import org.apache.geode.cache.client.Pool; import org.apache.geode.pdx.PdxSerializer; import org.springframework.beans.BeansException; -import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.config.BeanPostProcessor; -import org.springframework.beans.factory.config.ConfigurableBeanFactory; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.DependsOn; @@ -60,7 +54,6 @@ import org.springframework.data.gemfire.config.xml.GemfireConstants; import org.springframework.data.gemfire.util.ArrayUtils; import org.springframework.session.Session; import org.springframework.session.SessionRepository; -import org.springframework.session.config.annotation.web.http.SpringHttpSessionConfiguration; import org.springframework.session.data.gemfire.AbstractGemFireOperationsSessionRepository.GemFireSession; import org.springframework.session.data.gemfire.GemFireOperationsSessionRepository; import org.springframework.session.data.gemfire.config.annotation.web.http.support.GemFireCacheTypeAwareRegionFactoryBean; @@ -72,7 +65,6 @@ import org.springframework.session.data.gemfire.serialization.pdx.provider.PdxSe import org.springframework.session.data.gemfire.serialization.pdx.support.ComposablePdxSerializer; import org.springframework.session.data.gemfire.serialization.pdx.support.PdxSerializerSessionSerializerAdapter; import org.springframework.session.data.gemfire.support.GemFireUtils; -import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; /** @@ -102,6 +94,7 @@ import org.springframework.util.StringUtils; * @see org.springframework.session.Session * @see org.springframework.session.config.annotation.web.http.SpringHttpSessionConfiguration * @see org.springframework.session.data.gemfire.GemFireOperationsSessionRepository + * @see org.springframework.session.data.gemfire.config.annotation.web.http.AbstractGemFireHttpSessionConfiguration * @see org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession * @see org.springframework.session.data.gemfire.config.annotation.web.http.support.GemFireCacheTypeAwareRegionFactoryBean * @see org.springframework.session.data.gemfire.config.annotation.web.http.support.SessionAttributesIndexFactoryBean @@ -109,8 +102,7 @@ import org.springframework.util.StringUtils; */ @Configuration @SuppressWarnings("unused") -public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfiguration - implements BeanClassLoaderAware, ImportAware { +public class GemFireHttpSessionConfiguration extends AbstractGemFireHttpSessionConfiguration implements ImportAware { /** * Default maximum interval in seconds in which a {@link Session} can remain inactive before it expires. @@ -162,10 +154,6 @@ public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfigurat private int maxInactiveIntervalInSeconds = DEFAULT_MAX_INACTIVE_INTERVAL_IN_SECONDS; - private ApplicationContext applicationContext; - - private ClassLoader beanClassLoader; - private ClientRegionShortcut clientRegionShortcut = DEFAULT_CLIENT_REGION_SHORTCUT; private RegionShortcut serverRegionShortcut = DEFAULT_SERVER_REGION_SHORTCUT; @@ -178,70 +166,6 @@ public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfigurat private String[] indexableSessionAttributes = DEFAULT_INDEXABLE_SESSION_ATTRIBUTES; - /** - * Sets a reference the Spring {@link ApplicationContext}. - * - * @param applicationContext reference to the Spring {@link ApplicationContext}. - * @throws BeansException if the reference cannot be stored. - * @see org.springframework.context.ApplicationContext - */ - @Override - public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { - super.setApplicationContext(applicationContext); - this.applicationContext = applicationContext; - } - - /** - * Returns a reference to the Spring {@link ApplicationContext}. - * - * @return a reference to the Spring {@link ApplicationContext}. - * @see org.springframework.context.ApplicationContext - */ - protected ApplicationContext getApplicationContext() { - return Optional.ofNullable(this.applicationContext) - .orElseThrow(() -> newIllegalStateException("The ApplicationContext was not properly configured")); - } - - /** - * Sets a reference to the {@link ClassLoader} used by the Spring container to load bean {@link Class class types}. - * - * @param beanClassLoader {@link ClassLoader} used by the Spring container to load bean {@link Class class types}. - * @see org.springframework.beans.factory.BeanClassLoaderAware#setBeanClassLoader(ClassLoader) - * @see java.lang.ClassLoader - */ - public void setBeanClassLoader(ClassLoader beanClassLoader) { - this.beanClassLoader = beanClassLoader; - } - - /** - * Returns a reference to the {@link ClassLoader} used by the Spring container to load bean - * {@link Class class types}. - * - * @return the {@link ClassLoader} used by the Spring container to load bean {@link Class class types}. - * @see java.lang.ClassLoader - */ - protected ClassLoader getBeanClassLoader() { - return this.beanClassLoader; - } - - /** - * Returns a reference to the Spring container {@link ConfigurableBeanFactory}. - * - * @return a reference to the Spring container {@link ConfigurableBeanFactory}. - * @see org.springframework.beans.factory.config.ConfigurableBeanFactory - * @see #getApplicationContext() - */ - protected ConfigurableBeanFactory getBeanFactory() { - - ApplicationContext applicationContext = getApplicationContext(); - - return Optional.of(applicationContext) - .filter(it -> it instanceof ConfigurableApplicationContext) - .map(it -> ((ConfigurableApplicationContext) it).getBeanFactory()) - .orElseThrow(() -> newIllegalStateException("Unable to resolve a reference to a [%1$s] from a [%2$s]", - ConfigurableBeanFactory.class.getName(), ObjectUtils.nullSafeClassName(applicationContext))); - } - /** * Gets the {@link ClientRegionShortcut} used to configure the data management policy of the {@link ClientCache} * {@link Region} that will store {@link Session} state. @@ -449,24 +373,43 @@ public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfigurat AnnotationAttributes.fromMap(importMetadata.getAnnotationAttributes( EnableGemFireHttpSession.class.getName())); - setClientRegionShortcut(ClientRegionShortcut.class.cast( - enableGemFireHttpSessionAttributes.getEnum("clientRegionShortcut"))); + ClientRegionShortcut defaultClientRegionShortcut = ClientRegionShortcut.class + .cast(enableGemFireHttpSessionAttributes.getEnum("clientRegionShortcut")); - setIndexableSessionAttributes( - enableGemFireHttpSessionAttributes.getStringArray("indexableSessionAttributes")); + setClientRegionShortcut(resolveProperty(clientRegionShortcutPropertyName(), + ClientRegionShortcut.class, defaultClientRegionShortcut)); - setMaxInactiveIntervalInSeconds( - enableGemFireHttpSessionAttributes.getNumber("maxInactiveIntervalInSeconds").intValue()); + String[] defaultIndexableSessionAttributes = + enableGemFireHttpSessionAttributes.getStringArray("indexableSessionAttributes"); - setPoolName(enableGemFireHttpSessionAttributes.getString("poolName")); + setIndexableSessionAttributes(resolveProperty(indexableSessionAttributesPropertyName(), + defaultIndexableSessionAttributes)); - setServerRegionShortcut(RegionShortcut.class.cast( - enableGemFireHttpSessionAttributes.getEnum("serverRegionShortcut"))); + Integer defaultMaxInactiveIntervalInSeconds = + enableGemFireHttpSessionAttributes.getNumber("maxInactiveIntervalInSeconds").intValue(); - setSessionRegionName(enableGemFireHttpSessionAttributes.getString("regionName")); + setMaxInactiveIntervalInSeconds(resolveProperty(maxInactiveIntervalInSecondsPropertyName(), + defaultMaxInactiveIntervalInSeconds)); - setSessionSerializerBeanName( - enableGemFireHttpSessionAttributes.getString("sessionSerializerBeanName")); + String defaultPoolName = enableGemFireHttpSessionAttributes.getString("poolName"); + + setPoolName(resolveProperty(poolNamePropertyName(), defaultPoolName)); + + String defaultSessionRegionName = enableGemFireHttpSessionAttributes.getString("regionName"); + + setSessionRegionName(resolveProperty(sessionRegionNamePropertyName(), defaultSessionRegionName)); + + RegionShortcut defaultServerRegionShortcut = RegionShortcut.class + .cast(enableGemFireHttpSessionAttributes.getEnum("serverRegionShortcut")); + + setServerRegionShortcut(resolveProperty(serverRegionShortcutPropertyName(), + RegionShortcut.class, defaultServerRegionShortcut)); + + String defaultSessionSerializerBeanName = + enableGemFireHttpSessionAttributes.getString("sessionSerializerBeanName"); + + setSessionSerializerBeanName(resolveProperty(sessionSerializerBeanNamePropertyName(), + defaultSessionSerializerBeanName)); } @PostConstruct @@ -617,9 +560,9 @@ public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfigurat */ boolean isExpirationAllowed(GemFireCache gemfireCache) { - return !(GemFireUtils.isClient(gemfireCache) + return !GemFireUtils.isClient(gemfireCache) ? GemFireUtils.isProxy(getClientRegionShortcut()) - : GemFireUtils.isProxy(getServerRegionShortcut())); + : GemFireUtils.isProxy(getServerRegionShortcut()); } /**