Add annotation configuration support to configure custom session expiration policies.
Resolves gh-5.
This commit is contained in:
@@ -32,6 +32,9 @@ import org.springframework.session.config.annotation.web.http.SpringHttpSessionC
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
@@ -59,6 +62,8 @@ public abstract class AbstractGemFireHttpSessionConfiguration extends SpringHttp
|
||||
|
||||
private Environment environment;
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
/**
|
||||
* Sets a reference the Spring {@link ApplicationContext}.
|
||||
*
|
||||
@@ -68,7 +73,9 @@ public abstract class AbstractGemFireHttpSessionConfiguration extends SpringHttp
|
||||
*/
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
|
||||
super.setApplicationContext(applicationContext);
|
||||
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
@@ -144,6 +151,16 @@ public abstract class AbstractGemFireHttpSessionConfiguration extends SpringHttp
|
||||
return this.environment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a reference to the configured SLF4J {@link Logger}.
|
||||
*
|
||||
* @return a reference to the configured SLF4J {@link Logger}.
|
||||
* @see org.slf4j.Logger
|
||||
*/
|
||||
protected Logger getLogger() {
|
||||
return this.logger;
|
||||
}
|
||||
|
||||
protected String clientRegionShortcutPropertyName() {
|
||||
return propertyName("cache.client.region.shortcut");
|
||||
}
|
||||
@@ -168,6 +185,10 @@ public abstract class AbstractGemFireHttpSessionConfiguration extends SpringHttp
|
||||
return propertyName(String.format("session.%s", propertyNameSuffix));
|
||||
}
|
||||
|
||||
protected String sessionExpirationPolicyBeanNamePropertyName() {
|
||||
return sessionPropertyName("expiration.bean-name");
|
||||
}
|
||||
|
||||
protected String sessionRegionNamePropertyName() {
|
||||
return sessionPropertyName("region.name");
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ import org.apache.geode.cache.client.Pool;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.data.gemfire.expiration.SessionExpirationPolicy;
|
||||
import org.springframework.session.data.gemfire.serialization.SessionSerializer;
|
||||
import org.springframework.session.web.http.SessionRepositoryFilter;
|
||||
|
||||
@@ -178,11 +179,27 @@ public @interface EnableGemFireHttpSession {
|
||||
*/
|
||||
RegionShortcut serverRegionShortcut() default RegionShortcut.PARTITION;
|
||||
|
||||
/**
|
||||
* Defines the name of the bean referring to the {@link SessionExpirationPolicy} used to configure
|
||||
* the {@link Session} expiration logic and strategy.
|
||||
*
|
||||
* The {@link Object bean} referred to by its {@link String name} must be of type {@link SessionExpirationPolicy}.
|
||||
*
|
||||
* Defaults to unset.
|
||||
|
||||
* Use the {@literal spring.session.data.gemfire.session.expiration.bean-name} in Spring Boot
|
||||
* {@literal application.properties}.
|
||||
*
|
||||
* @return a {@link String} containing the bean name of the configured {@link SessionExpirationPolicy}.
|
||||
* @see org.springframework.session.data.gemfire.expiration.SessionExpirationPolicy
|
||||
*/
|
||||
String sessionExpirationPolicyBeanName() default GemFireHttpSessionConfiguration.DEFAULT_SESSION_EXPIRATION_POLICY_BEAN_NAME;
|
||||
|
||||
/**
|
||||
* Defines the bean name of the {@link SessionSerializer} used to serialize {@link Session} state
|
||||
* between client and server or to disk when persisting or overflowing {@link Session} state.
|
||||
*
|
||||
* The bean referred to by its name must be of type {@link SessionSerializer}.
|
||||
* The {@link Object bean referred to by its {@link String name} must be of type {@link SessionSerializer}.
|
||||
*
|
||||
* Defaults to {@literal SessionPdxSerializer}.
|
||||
*
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.session.data.gemfire.config.annotation.web.http;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@@ -49,6 +50,7 @@ import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.data.gemfire.CacheFactoryBean;
|
||||
import org.springframework.data.gemfire.GemfireOperations;
|
||||
import org.springframework.data.gemfire.GemfireTemplate;
|
||||
import org.springframework.data.gemfire.GemfireUtils;
|
||||
import org.springframework.data.gemfire.IndexFactoryBean;
|
||||
import org.springframework.data.gemfire.IndexType;
|
||||
import org.springframework.data.gemfire.RegionAttributesFactoryBean;
|
||||
@@ -61,6 +63,9 @@ import org.springframework.session.data.gemfire.GemFireOperationsSessionReposito
|
||||
import org.springframework.session.data.gemfire.config.annotation.web.http.support.GemFireCacheTypeAwareRegionFactoryBean;
|
||||
import org.springframework.session.data.gemfire.config.annotation.web.http.support.SessionAttributesIndexFactoryBean;
|
||||
import org.springframework.session.data.gemfire.config.annotation.web.http.support.SpringSessionGemFireConfigurer;
|
||||
import org.springframework.session.data.gemfire.expiration.SessionExpirationPolicy;
|
||||
import org.springframework.session.data.gemfire.expiration.config.SessionExpirationTimeoutAwareBeanPostProcessor;
|
||||
import org.springframework.session.data.gemfire.expiration.support.SessionExpirationPolicyCustomExpiryAdapter;
|
||||
import org.springframework.session.data.gemfire.serialization.SessionSerializer;
|
||||
import org.springframework.session.data.gemfire.serialization.data.provider.DataSerializableSessionSerializer;
|
||||
import org.springframework.session.data.gemfire.serialization.data.support.DataSerializerSessionSerializerAdapter;
|
||||
@@ -87,20 +92,26 @@ import org.springframework.util.StringUtils;
|
||||
* @see org.springframework.context.annotation.Bean
|
||||
* @see org.springframework.context.annotation.Configuration
|
||||
* @see org.springframework.context.annotation.DependsOn
|
||||
* @see org.springframework.context.annotation.Import
|
||||
* @see org.springframework.context.annotation.ImportAware
|
||||
* @see org.springframework.core.annotation.AnnotationAttributes
|
||||
* @see org.springframework.core.type.AnnotationMetadata
|
||||
* @see org.springframework.data.gemfire.CacheFactoryBean
|
||||
* @see org.springframework.data.gemfire.GemfireOperations
|
||||
* @see org.springframework.data.gemfire.GemfireTemplate
|
||||
* @see org.springframework.data.gemfire.IndexFactoryBean
|
||||
* @see org.springframework.data.gemfire.RegionAttributesFactoryBean
|
||||
* @see org.springframework.session.Session
|
||||
* @see org.springframework.session.SessionRepository
|
||||
* @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
|
||||
* @see org.springframework.session.data.gemfire.expiration.SessionExpirationPolicy
|
||||
* @see org.springframework.session.data.gemfire.expiration.config.SessionExpirationTimeoutAwareBeanPostProcessor
|
||||
* @see org.springframework.session.data.gemfire.serialization.SessionSerializer
|
||||
* @since 1.1.0
|
||||
*/
|
||||
@Configuration
|
||||
@@ -136,6 +147,11 @@ public class GemFireHttpSessionConfiguration extends AbstractGemFireHttpSessionC
|
||||
*/
|
||||
public static final String DEFAULT_POOL_NAME = GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME;
|
||||
|
||||
/**
|
||||
* Default name for the {@link SessionExpirationPolicy} bean.
|
||||
*/
|
||||
public static final String DEFAULT_SESSION_EXPIRATION_POLICY_BEAN_NAME = "";
|
||||
|
||||
/**
|
||||
* Default name of (Client)Cache {@link Region} used to store {@link Session} state.
|
||||
*/
|
||||
@@ -165,6 +181,8 @@ public class GemFireHttpSessionConfiguration extends AbstractGemFireHttpSessionC
|
||||
|
||||
private String sessionRegionName = DEFAULT_SESSION_REGION_NAME;
|
||||
|
||||
private String sessionExpirationPolicyBeanName = DEFAULT_SESSION_EXPIRATION_POLICY_BEAN_NAME;
|
||||
|
||||
private String sessionSerializerBeanName = DEFAULT_SESSION_SERIALIZER_BEAN_NAME;
|
||||
|
||||
private String[] indexableSessionAttributes = DEFAULT_INDEXABLE_SESSION_ATTRIBUTES;
|
||||
@@ -195,7 +213,9 @@ public class GemFireHttpSessionConfiguration extends AbstractGemFireHttpSessionC
|
||||
* @see org.apache.geode.cache.client.ClientRegionShortcut
|
||||
*/
|
||||
protected ClientRegionShortcut getClientRegionShortcut() {
|
||||
return Optional.ofNullable(this.clientRegionShortcut).orElse(DEFAULT_CLIENT_REGION_SHORTCUT);
|
||||
|
||||
return Optional.ofNullable(this.clientRegionShortcut)
|
||||
.orElse(DEFAULT_CLIENT_REGION_SHORTCUT);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -216,7 +236,9 @@ public class GemFireHttpSessionConfiguration extends AbstractGemFireHttpSessionC
|
||||
* for which an Index will be created. Defaults to an empty array if unspecified.
|
||||
*/
|
||||
protected String[] getIndexableSessionAttributes() {
|
||||
return Optional.ofNullable(this.indexableSessionAttributes).orElse(DEFAULT_INDEXABLE_SESSION_ATTRIBUTES);
|
||||
|
||||
return Optional.ofNullable(this.indexableSessionAttributes)
|
||||
.orElse(DEFAULT_INDEXABLE_SESSION_ATTRIBUTES);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -259,7 +281,10 @@ public class GemFireHttpSessionConfiguration extends AbstractGemFireHttpSessionC
|
||||
* @see org.apache.geode.cache.client.Pool#getName()
|
||||
*/
|
||||
protected String getPoolName() {
|
||||
return Optional.ofNullable(this.poolName).filter(StringUtils::hasText).orElse(DEFAULT_POOL_NAME);
|
||||
|
||||
return Optional.ofNullable(this.poolName)
|
||||
.filter(StringUtils::hasText)
|
||||
.orElse(DEFAULT_POOL_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -288,7 +313,19 @@ public class GemFireHttpSessionConfiguration extends AbstractGemFireHttpSessionC
|
||||
* @see org.apache.geode.cache.RegionShortcut
|
||||
*/
|
||||
protected RegionShortcut getServerRegionShortcut() {
|
||||
return Optional.ofNullable(this.serverRegionShortcut).orElse(DEFAULT_SERVER_REGION_SHORTCUT);
|
||||
|
||||
return Optional.ofNullable(this.serverRegionShortcut)
|
||||
.orElse(DEFAULT_SERVER_REGION_SHORTCUT);
|
||||
}
|
||||
|
||||
public void setSessionExpirationPolicyBeanName(String sessionExpirationPolicyBeanName) {
|
||||
this.sessionExpirationPolicyBeanName = sessionExpirationPolicyBeanName;
|
||||
}
|
||||
|
||||
protected Optional<String> getSessionExpirationPolicyBeanName() {
|
||||
|
||||
return Optional.ofNullable(this.sessionExpirationPolicyBeanName)
|
||||
.filter(StringUtils::hasText);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -310,7 +347,9 @@ public class GemFireHttpSessionConfiguration extends AbstractGemFireHttpSessionC
|
||||
* @see org.apache.geode.cache.Region#getName()
|
||||
*/
|
||||
protected String getSessionRegionName() {
|
||||
return Optional.ofNullable(this.sessionRegionName).filter(StringUtils::hasText)
|
||||
|
||||
return Optional.ofNullable(this.sessionRegionName)
|
||||
.filter(StringUtils::hasText)
|
||||
.orElse(DEFAULT_SESSION_REGION_NAME);
|
||||
}
|
||||
|
||||
@@ -346,7 +385,9 @@ public class GemFireHttpSessionConfiguration extends AbstractGemFireHttpSessionC
|
||||
* @see org.springframework.session.data.gemfire.serialization.SessionSerializer
|
||||
*/
|
||||
protected String getSessionSerializerBeanName() {
|
||||
return Optional.ofNullable(this.sessionSerializerBeanName).filter(StringUtils::hasText)
|
||||
|
||||
return Optional.ofNullable(this.sessionSerializerBeanName)
|
||||
.filter(StringUtils::hasText)
|
||||
.orElse(DEFAULT_SESSION_SERIALIZER_BEAN_NAME);
|
||||
}
|
||||
|
||||
@@ -376,45 +417,83 @@ public class GemFireHttpSessionConfiguration extends AbstractGemFireHttpSessionC
|
||||
AnnotationAttributes.fromMap(importMetadata.getAnnotationAttributes(
|
||||
EnableGemFireHttpSession.class.getName()));
|
||||
|
||||
configureClientRegionShortcut(enableGemFireHttpSessionAttributes);
|
||||
configureIndexableSessionAttributes(enableGemFireHttpSessionAttributes);
|
||||
configureMaxInactiveIntervalInSeconds(enableGemFireHttpSessionAttributes);
|
||||
configurePoolName(enableGemFireHttpSessionAttributes);
|
||||
configureServerRegionShortcut(enableGemFireHttpSessionAttributes);
|
||||
configureSessionExpirationPolicyBeanName(enableGemFireHttpSessionAttributes);
|
||||
configureSessionRegionName(enableGemFireHttpSessionAttributes);
|
||||
configureSessionSerializerBeanName(enableGemFireHttpSessionAttributes);
|
||||
|
||||
applySpringSessionGemFireConfigurer();
|
||||
}
|
||||
|
||||
private void configureClientRegionShortcut(AnnotationAttributes enableGemFireHttpSessionAttributes) {
|
||||
|
||||
ClientRegionShortcut defaultClientRegionShortcut =
|
||||
enableGemFireHttpSessionAttributes.getEnum("clientRegionShortcut");
|
||||
|
||||
setClientRegionShortcut(resolveProperty(clientRegionShortcutPropertyName(),
|
||||
ClientRegionShortcut.class, defaultClientRegionShortcut));
|
||||
}
|
||||
|
||||
private void configureIndexableSessionAttributes(AnnotationAttributes enableGemFireHttpSessionAttributes) {
|
||||
|
||||
String[] defaultIndexableSessionAttributes =
|
||||
enableGemFireHttpSessionAttributes.getStringArray("indexableSessionAttributes");
|
||||
|
||||
setIndexableSessionAttributes(resolveProperty(indexableSessionAttributesPropertyName(),
|
||||
defaultIndexableSessionAttributes));
|
||||
}
|
||||
|
||||
private void configureMaxInactiveIntervalInSeconds(AnnotationAttributes enableGemFireHttpSessionAttributes) {
|
||||
|
||||
Integer defaultMaxInactiveIntervalInSeconds =
|
||||
enableGemFireHttpSessionAttributes.getNumber("maxInactiveIntervalInSeconds").intValue();
|
||||
|
||||
setMaxInactiveIntervalInSeconds(resolveProperty(maxInactiveIntervalInSecondsPropertyName(),
|
||||
defaultMaxInactiveIntervalInSeconds));
|
||||
}
|
||||
|
||||
private void configurePoolName(AnnotationAttributes enableGemFireHttpSessionAttributes) {
|
||||
String defaultPoolName = enableGemFireHttpSessionAttributes.getString("poolName");
|
||||
|
||||
setPoolName(resolveProperty(poolNamePropertyName(), defaultPoolName));
|
||||
}
|
||||
|
||||
String defaultSessionRegionName = enableGemFireHttpSessionAttributes.getString("regionName");
|
||||
|
||||
setSessionRegionName(resolveProperty(sessionRegionNamePropertyName(), defaultSessionRegionName));
|
||||
private void configureServerRegionShortcut(AnnotationAttributes enableGemFireHttpSessionAttributes) {
|
||||
|
||||
RegionShortcut defaultServerRegionShortcut =
|
||||
enableGemFireHttpSessionAttributes.getEnum("serverRegionShortcut");
|
||||
|
||||
setServerRegionShortcut(resolveProperty(serverRegionShortcutPropertyName(),
|
||||
RegionShortcut.class, defaultServerRegionShortcut));
|
||||
}
|
||||
|
||||
private void configureSessionExpirationPolicyBeanName(AnnotationAttributes enableGemFireHttpSessionAttributes) {
|
||||
|
||||
String defaultSessionExpirationPolicyBeanName =
|
||||
enableGemFireHttpSessionAttributes.getString("sessionExpirationPolicyBeanName");
|
||||
|
||||
setSessionExpirationPolicyBeanName(resolveProperty(sessionExpirationPolicyBeanNamePropertyName(),
|
||||
defaultSessionExpirationPolicyBeanName));
|
||||
}
|
||||
|
||||
private void configureSessionRegionName(AnnotationAttributes enableGemFireHttpSessionAttributes) {
|
||||
|
||||
String defaultSessionRegionName = enableGemFireHttpSessionAttributes.getString("regionName");
|
||||
|
||||
setSessionRegionName(resolveProperty(sessionRegionNamePropertyName(), defaultSessionRegionName));
|
||||
}
|
||||
|
||||
private void configureSessionSerializerBeanName(AnnotationAttributes enableGemFireHttpSessionAttributes) {
|
||||
|
||||
String defaultSessionSerializerBeanName =
|
||||
enableGemFireHttpSessionAttributes.getString("sessionSerializerBeanName");
|
||||
|
||||
setSessionSerializerBeanName(resolveProperty(sessionSerializerBeanNamePropertyName(),
|
||||
defaultSessionSerializerBeanName));
|
||||
|
||||
applySpringSessionGemFireConfigurer();
|
||||
}
|
||||
|
||||
private Optional<SpringSessionGemFireConfigurer> resolveSpringSessionGemFireConfigurer() {
|
||||
@@ -454,6 +533,36 @@ public class GemFireHttpSessionConfiguration extends AbstractGemFireHttpSessionC
|
||||
getBeanFactory().registerAlias(getSessionSerializerBeanName(), SESSION_SERIALIZER_BEAN_ALIAS);
|
||||
}
|
||||
|
||||
@Bean
|
||||
BeanPostProcessor sessionExpirationTimeoutAwareBeanPostProcessor() {
|
||||
|
||||
Duration expirationTimeout = Duration.ofSeconds(getMaxInactiveIntervalInSeconds());
|
||||
|
||||
return new SessionExpirationTimeoutAwareBeanPostProcessor(expirationTimeout);
|
||||
}
|
||||
|
||||
private Optional<SessionExpirationPolicy> resolveSessionExpirationPolicy() {
|
||||
|
||||
Optional<String> sessionExpirationPolicyBeanName = getSessionExpirationPolicyBeanName();
|
||||
|
||||
if (sessionExpirationPolicyBeanName.isPresent()) {
|
||||
if (getApplicationContext().containsBean(sessionExpirationPolicyBeanName.get())) {
|
||||
return Optional.of(getApplicationContext()
|
||||
.getBean(sessionExpirationPolicyBeanName.get(), SessionExpirationPolicy.class));
|
||||
}
|
||||
else {
|
||||
|
||||
String logMessage = "No Bean with name [{}] and type [{}] was configured;"
|
||||
+ " Defaulting to Expiration policy configured for Region [{}]";
|
||||
|
||||
getLogger().warn(logMessage, sessionExpirationPolicyBeanName.get(),
|
||||
SessionExpirationPolicy.class.getName(), getSessionRegionName());
|
||||
}
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Bean
|
||||
BeanPostProcessor sessionSerializerConfigurationBeanPostProcessor() {
|
||||
|
||||
@@ -576,9 +685,23 @@ public class GemFireHttpSessionConfiguration extends AbstractGemFireHttpSessionC
|
||||
regionAttributes.setValueConstraint(SESSION_REGION_VALUE_CONSTRAINT);
|
||||
|
||||
if (isExpirationAllowed(gemfireCache)) {
|
||||
|
||||
regionAttributes.setStatisticsEnabled(true);
|
||||
regionAttributes.setEntryIdleTimeout(
|
||||
new ExpirationAttributes(Math.max(getMaxInactiveIntervalInSeconds(), 0), ExpirationAction.INVALIDATE));
|
||||
|
||||
Optional<SessionExpirationPolicy> sessionExpirationPolicy = resolveSessionExpirationPolicy();
|
||||
|
||||
if (sessionExpirationPolicy.isPresent()) {
|
||||
regionAttributes.setCustomEntryIdleTimeout(new SessionExpirationPolicyCustomExpiryAdapter(
|
||||
sessionExpirationPolicy.get()));
|
||||
}
|
||||
else {
|
||||
regionAttributes.setEntryIdleTimeout(new ExpirationAttributes(
|
||||
Math.max(getMaxInactiveIntervalInSeconds(), 0), ExpirationAction.INVALIDATE));
|
||||
}
|
||||
}
|
||||
else {
|
||||
getLogger().info("Expiration is not allowed on Regions with a data management policy of {}",
|
||||
GemfireUtils.isClient(gemfireCache) ? getClientRegionShortcut() : getServerRegionShortcut());
|
||||
}
|
||||
|
||||
return regionAttributes;
|
||||
@@ -634,7 +757,7 @@ public class GemFireHttpSessionConfiguration extends AbstractGemFireHttpSessionC
|
||||
*/
|
||||
@Bean
|
||||
public GemFireOperationsSessionRepository sessionRepository(
|
||||
@Qualifier("sessionRegionTemplate") GemfireOperations gemfireOperations) {
|
||||
@Qualifier("sessionRegionTemplate") GemfireOperations gemfireOperations) {
|
||||
|
||||
GemFireOperationsSessionRepository sessionRepository =
|
||||
new GemFireOperationsSessionRepository(gemfireOperations);
|
||||
|
||||
Reference in New Issue
Block a user