Use simple Redis repository by default
Closes gh-1711
This commit is contained in:
committed by
Eleftheria Stein-Kousathana
parent
14ecf21c94
commit
8582b9706d
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2014-2021 the original author or authors.
|
* Copyright 2014-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -691,7 +691,7 @@ class RedisIndexedSessionRepositoryITests extends AbstractRedisITests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableRedisHttpSession(redisNamespace = "RedisIndexedSessionRepositoryITests")
|
@EnableRedisHttpSession(redisNamespace = "RedisIndexedSessionRepositoryITests", enableIndexingAndEvents = true)
|
||||||
static class Config extends BaseConfig {
|
static class Config extends BaseConfig {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2014-2019 the original author or authors.
|
* Copyright 2014-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -26,13 +26,10 @@ import org.junit.jupiter.api.Test;
|
|||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
|
||||||
import org.springframework.data.redis.core.RedisTemplate;
|
|
||||||
import org.springframework.session.MapSession;
|
import org.springframework.session.MapSession;
|
||||||
import org.springframework.session.config.annotation.web.http.EnableSpringHttpSession;
|
|
||||||
import org.springframework.session.data.redis.RedisSessionRepository.RedisSession;
|
import org.springframework.session.data.redis.RedisSessionRepository.RedisSession;
|
||||||
|
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
|
||||||
import org.springframework.test.context.ContextConfiguration;
|
import org.springframework.test.context.ContextConfiguration;
|
||||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||||
import org.springframework.test.context.web.WebAppConfiguration;
|
import org.springframework.test.context.web.WebAppConfiguration;
|
||||||
@@ -223,17 +220,9 @@ class RedisSessionRepositoryITests extends AbstractRedisITests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableSpringHttpSession
|
@EnableRedisHttpSession
|
||||||
static class Config extends BaseConfig {
|
static class Config extends BaseConfig {
|
||||||
|
|
||||||
@Bean
|
|
||||||
RedisSessionRepository sessionRepository(RedisConnectionFactory redisConnectionFactory) {
|
|
||||||
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
|
|
||||||
redisTemplate.setConnectionFactory(redisConnectionFactory);
|
|
||||||
redisTemplate.afterPropertiesSet();
|
|
||||||
return new RedisSessionRepository(redisTemplate);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2014-2019 the original author or authors.
|
* Copyright 2014-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -113,7 +113,7 @@ class EnableRedisHttpSessionExpireSessionDestroyedTests<S extends Session> exten
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 1)
|
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 1, enableIndexingAndEvents = true)
|
||||||
static class Config extends BaseConfig {
|
static class Config extends BaseConfig {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2014-2019 the original author or authors.
|
* Copyright 2014-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -101,7 +101,7 @@ class RedisListenerContainerTaskExecutorITests extends AbstractRedisITests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableRedisHttpSession(redisNamespace = "RedisListenerContainerTaskExecutorITests")
|
@EnableRedisHttpSession(redisNamespace = "RedisListenerContainerTaskExecutorITests", enableIndexingAndEvents = true)
|
||||||
static class Config extends BaseConfig {
|
static class Config extends BaseConfig {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2014-2019 the original author or authors.
|
* Copyright 2014-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -33,6 +33,7 @@ import org.springframework.session.SessionRepository;
|
|||||||
import org.springframework.session.config.annotation.web.http.EnableSpringHttpSession;
|
import org.springframework.session.config.annotation.web.http.EnableSpringHttpSession;
|
||||||
import org.springframework.session.data.redis.RedisFlushMode;
|
import org.springframework.session.data.redis.RedisFlushMode;
|
||||||
import org.springframework.session.data.redis.RedisIndexedSessionRepository;
|
import org.springframework.session.data.redis.RedisIndexedSessionRepository;
|
||||||
|
import org.springframework.session.data.redis.RedisSessionRepository;
|
||||||
import org.springframework.session.web.http.SessionRepositoryFilter;
|
import org.springframework.session.web.http.SessionRepositoryFilter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -54,7 +55,8 @@ import org.springframework.session.web.http.SessionRepositoryFilter;
|
|||||||
* }
|
* }
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* More advanced configurations can extend {@link RedisHttpSessionConfiguration} instead.
|
* More advanced configurations can extend {@link RedisHttpSessionConfiguration} or
|
||||||
|
* {@link RedisIndexedHttpSessionConfiguration} instead.
|
||||||
*
|
*
|
||||||
* @author Rob Winch
|
* @author Rob Winch
|
||||||
* @author Vedran Pavic
|
* @author Vedran Pavic
|
||||||
@@ -64,7 +66,7 @@ import org.springframework.session.web.http.SessionRepositoryFilter;
|
|||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Target(ElementType.TYPE)
|
@Target(ElementType.TYPE)
|
||||||
@Documented
|
@Documented
|
||||||
@Import(RedisHttpSessionConfiguration.class)
|
@Import(RedisHttpSessionConfigurationSelector.class)
|
||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
public @interface EnableRedisHttpSession {
|
public @interface EnableRedisHttpSession {
|
||||||
|
|
||||||
@@ -113,13 +115,6 @@ public @interface EnableRedisHttpSession {
|
|||||||
*/
|
*/
|
||||||
FlushMode flushMode() default FlushMode.ON_SAVE;
|
FlushMode flushMode() default FlushMode.ON_SAVE;
|
||||||
|
|
||||||
/**
|
|
||||||
* The cron expression for expired session cleanup job. By default runs every minute.
|
|
||||||
* @return the session cleanup cron expression
|
|
||||||
* @since 2.0.0
|
|
||||||
*/
|
|
||||||
String cleanupCron() default RedisHttpSessionConfiguration.DEFAULT_CLEANUP_CRON;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save mode for the session. The default is {@link SaveMode#ON_SET_ATTRIBUTE}, which
|
* Save mode for the session. The default is {@link SaveMode#ON_SET_ATTRIBUTE}, which
|
||||||
* only saves changes made to session.
|
* only saves changes made to session.
|
||||||
@@ -128,4 +123,13 @@ public @interface EnableRedisHttpSession {
|
|||||||
*/
|
*/
|
||||||
SaveMode saveMode() default SaveMode.ON_SET_ATTRIBUTE;
|
SaveMode saveMode() default SaveMode.ON_SET_ATTRIBUTE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate whether the {@link SessionRepository} should publish session events and
|
||||||
|
* support fetching sessions by index. If true, a
|
||||||
|
* {@link RedisIndexedSessionRepository} will be used in place of
|
||||||
|
* {@link RedisSessionRepository}. This will result in slower performance.
|
||||||
|
* @return true if indexing and events should be enabled, false otherwise
|
||||||
|
*/
|
||||||
|
boolean enableIndexingAndEvents() default false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2014-2019 the original author or authors.
|
* Copyright 2014-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -16,55 +16,35 @@
|
|||||||
|
|
||||||
package org.springframework.session.data.redis.config.annotation.web.http;
|
package org.springframework.session.data.redis.config.annotation.web.http;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.time.Duration;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.apache.commons.logging.LogFactory;
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||||
import org.springframework.beans.factory.InitializingBean;
|
|
||||||
import org.springframework.beans.factory.ObjectProvider;
|
import org.springframework.beans.factory.ObjectProvider;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.context.ApplicationEventPublisher;
|
|
||||||
import org.springframework.context.EmbeddedValueResolverAware;
|
import org.springframework.context.EmbeddedValueResolverAware;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.ImportAware;
|
import org.springframework.context.annotation.ImportAware;
|
||||||
import org.springframework.core.annotation.AnnotationAttributes;
|
import org.springframework.core.annotation.AnnotationAttributes;
|
||||||
import org.springframework.core.type.AnnotationMetadata;
|
import org.springframework.core.type.AnnotationMetadata;
|
||||||
import org.springframework.data.redis.connection.RedisConnection;
|
|
||||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||||
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
|
|
||||||
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
|
|
||||||
import org.springframework.data.redis.core.RedisTemplate;
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
import org.springframework.data.redis.listener.ChannelTopic;
|
|
||||||
import org.springframework.data.redis.listener.PatternTopic;
|
|
||||||
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
|
|
||||||
import org.springframework.data.redis.serializer.RedisSerializer;
|
import org.springframework.data.redis.serializer.RedisSerializer;
|
||||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
|
||||||
import org.springframework.scheduling.annotation.SchedulingConfigurer;
|
|
||||||
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
|
|
||||||
import org.springframework.session.FlushMode;
|
import org.springframework.session.FlushMode;
|
||||||
import org.springframework.session.IndexResolver;
|
|
||||||
import org.springframework.session.MapSession;
|
import org.springframework.session.MapSession;
|
||||||
import org.springframework.session.SaveMode;
|
import org.springframework.session.SaveMode;
|
||||||
import org.springframework.session.Session;
|
|
||||||
import org.springframework.session.config.SessionRepositoryCustomizer;
|
import org.springframework.session.config.SessionRepositoryCustomizer;
|
||||||
import org.springframework.session.config.annotation.web.http.SpringHttpSessionConfiguration;
|
import org.springframework.session.config.annotation.web.http.SpringHttpSessionConfiguration;
|
||||||
import org.springframework.session.data.redis.RedisFlushMode;
|
import org.springframework.session.data.redis.RedisFlushMode;
|
||||||
import org.springframework.session.data.redis.RedisIndexedSessionRepository;
|
import org.springframework.session.data.redis.RedisSessionRepository;
|
||||||
import org.springframework.session.data.redis.config.ConfigureNotifyKeyspaceEventsAction;
|
|
||||||
import org.springframework.session.data.redis.config.ConfigureRedisAction;
|
|
||||||
import org.springframework.session.data.redis.config.annotation.SpringSessionRedisConnectionFactory;
|
import org.springframework.session.data.redis.config.annotation.SpringSessionRedisConnectionFactory;
|
||||||
import org.springframework.session.web.http.SessionRepositoryFilter;
|
import org.springframework.session.web.http.SessionRepositoryFilter;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.ClassUtils;
|
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
import org.springframework.util.StringValueResolver;
|
import org.springframework.util.StringValueResolver;
|
||||||
|
|
||||||
@@ -83,86 +63,39 @@ import org.springframework.util.StringValueResolver;
|
|||||||
public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguration
|
public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguration
|
||||||
implements BeanClassLoaderAware, EmbeddedValueResolverAware, ImportAware {
|
implements BeanClassLoaderAware, EmbeddedValueResolverAware, ImportAware {
|
||||||
|
|
||||||
static final String DEFAULT_CLEANUP_CRON = "0 * * * * *";
|
|
||||||
|
|
||||||
private Integer maxInactiveIntervalInSeconds = MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS;
|
private Integer maxInactiveIntervalInSeconds = MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS;
|
||||||
|
|
||||||
private String redisNamespace = RedisIndexedSessionRepository.DEFAULT_NAMESPACE;
|
private String redisNamespace = RedisSessionRepository.DEFAULT_KEY_NAMESPACE;
|
||||||
|
|
||||||
private FlushMode flushMode = FlushMode.ON_SAVE;
|
private FlushMode flushMode = FlushMode.ON_SAVE;
|
||||||
|
|
||||||
private SaveMode saveMode = SaveMode.ON_SET_ATTRIBUTE;
|
private SaveMode saveMode = SaveMode.ON_SET_ATTRIBUTE;
|
||||||
|
|
||||||
private String cleanupCron = DEFAULT_CLEANUP_CRON;
|
|
||||||
|
|
||||||
private ConfigureRedisAction configureRedisAction = new ConfigureNotifyKeyspaceEventsAction();
|
|
||||||
|
|
||||||
private RedisConnectionFactory redisConnectionFactory;
|
private RedisConnectionFactory redisConnectionFactory;
|
||||||
|
|
||||||
private IndexResolver<Session> indexResolver;
|
|
||||||
|
|
||||||
private RedisSerializer<Object> defaultRedisSerializer;
|
private RedisSerializer<Object> defaultRedisSerializer;
|
||||||
|
|
||||||
private ApplicationEventPublisher applicationEventPublisher;
|
private List<SessionRepositoryCustomizer<RedisSessionRepository>> sessionRepositoryCustomizers;
|
||||||
|
|
||||||
private Executor redisTaskExecutor;
|
|
||||||
|
|
||||||
private Executor redisSubscriptionExecutor;
|
|
||||||
|
|
||||||
private List<SessionRepositoryCustomizer<RedisIndexedSessionRepository>> sessionRepositoryCustomizers;
|
|
||||||
|
|
||||||
private ClassLoader classLoader;
|
private ClassLoader classLoader;
|
||||||
|
|
||||||
private StringValueResolver embeddedValueResolver;
|
private StringValueResolver embeddedValueResolver;
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public RedisIndexedSessionRepository sessionRepository() {
|
public RedisSessionRepository sessionRepository() {
|
||||||
RedisTemplate<Object, Object> redisTemplate = createRedisTemplate();
|
RedisTemplate<String, Object> redisTemplate = createRedisTemplate();
|
||||||
RedisIndexedSessionRepository sessionRepository = new RedisIndexedSessionRepository(redisTemplate);
|
RedisSessionRepository sessionRepository = new RedisSessionRepository(redisTemplate);
|
||||||
sessionRepository.setApplicationEventPublisher(this.applicationEventPublisher);
|
sessionRepository.setDefaultMaxInactiveInterval(Duration.ofSeconds(this.maxInactiveIntervalInSeconds));
|
||||||
if (this.indexResolver != null) {
|
|
||||||
sessionRepository.setIndexResolver(this.indexResolver);
|
|
||||||
}
|
|
||||||
if (this.defaultRedisSerializer != null) {
|
|
||||||
sessionRepository.setDefaultSerializer(this.defaultRedisSerializer);
|
|
||||||
}
|
|
||||||
sessionRepository.setDefaultMaxInactiveInterval(this.maxInactiveIntervalInSeconds);
|
|
||||||
if (StringUtils.hasText(this.redisNamespace)) {
|
if (StringUtils.hasText(this.redisNamespace)) {
|
||||||
sessionRepository.setRedisKeyNamespace(this.redisNamespace);
|
sessionRepository.setRedisKeyNamespace(this.redisNamespace);
|
||||||
}
|
}
|
||||||
sessionRepository.setFlushMode(this.flushMode);
|
sessionRepository.setFlushMode(this.flushMode);
|
||||||
sessionRepository.setSaveMode(this.saveMode);
|
sessionRepository.setSaveMode(this.saveMode);
|
||||||
int database = resolveDatabase();
|
|
||||||
sessionRepository.setDatabase(database);
|
|
||||||
this.sessionRepositoryCustomizers
|
this.sessionRepositoryCustomizers
|
||||||
.forEach((sessionRepositoryCustomizer) -> sessionRepositoryCustomizer.customize(sessionRepository));
|
.forEach((sessionRepositoryCustomizer) -> sessionRepositoryCustomizer.customize(sessionRepository));
|
||||||
return sessionRepository;
|
return sessionRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
|
||||||
public RedisMessageListenerContainer springSessionRedisMessageListenerContainer(
|
|
||||||
RedisIndexedSessionRepository sessionRepository) {
|
|
||||||
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
|
|
||||||
container.setConnectionFactory(this.redisConnectionFactory);
|
|
||||||
if (this.redisTaskExecutor != null) {
|
|
||||||
container.setTaskExecutor(this.redisTaskExecutor);
|
|
||||||
}
|
|
||||||
if (this.redisSubscriptionExecutor != null) {
|
|
||||||
container.setSubscriptionExecutor(this.redisSubscriptionExecutor);
|
|
||||||
}
|
|
||||||
container.addMessageListener(sessionRepository,
|
|
||||||
Arrays.asList(new ChannelTopic(sessionRepository.getSessionDeletedChannel()),
|
|
||||||
new ChannelTopic(sessionRepository.getSessionExpiredChannel())));
|
|
||||||
container.addMessageListener(sessionRepository,
|
|
||||||
Collections.singletonList(new PatternTopic(sessionRepository.getSessionCreatedChannelPrefix() + "*")));
|
|
||||||
return container;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public InitializingBean enableRedisKeyspaceNotificationsInitializer() {
|
|
||||||
return new EnableRedisKeyspaceNotificationsInitializer(this.redisConnectionFactory, this.configureRedisAction);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMaxInactiveIntervalInSeconds(int maxInactiveIntervalInSeconds) {
|
public void setMaxInactiveIntervalInSeconds(int maxInactiveIntervalInSeconds) {
|
||||||
this.maxInactiveIntervalInSeconds = maxInactiveIntervalInSeconds;
|
this.maxInactiveIntervalInSeconds = maxInactiveIntervalInSeconds;
|
||||||
}
|
}
|
||||||
@@ -186,20 +119,6 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
|
|||||||
this.saveMode = saveMode;
|
this.saveMode = saveMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCleanupCron(String cleanupCron) {
|
|
||||||
this.cleanupCron = cleanupCron;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the action to perform for configuring Redis.
|
|
||||||
* @param configureRedisAction the configureRedis to set. The default is
|
|
||||||
* {@link ConfigureNotifyKeyspaceEventsAction}.
|
|
||||||
*/
|
|
||||||
@Autowired(required = false)
|
|
||||||
public void setConfigureRedisAction(ConfigureRedisAction configureRedisAction) {
|
|
||||||
this.configureRedisAction = configureRedisAction;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public void setRedisConnectionFactory(
|
public void setRedisConnectionFactory(
|
||||||
@SpringSessionRedisConnectionFactory ObjectProvider<RedisConnectionFactory> springSessionRedisConnectionFactory,
|
@SpringSessionRedisConnectionFactory ObjectProvider<RedisConnectionFactory> springSessionRedisConnectionFactory,
|
||||||
@@ -217,31 +136,9 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
|
|||||||
this.defaultRedisSerializer = defaultRedisSerializer;
|
this.defaultRedisSerializer = defaultRedisSerializer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Autowired
|
|
||||||
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
|
|
||||||
this.applicationEventPublisher = applicationEventPublisher;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Autowired(required = false)
|
|
||||||
public void setIndexResolver(IndexResolver<Session> indexResolver) {
|
|
||||||
this.indexResolver = indexResolver;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Autowired(required = false)
|
|
||||||
@Qualifier("springSessionRedisTaskExecutor")
|
|
||||||
public void setRedisTaskExecutor(Executor redisTaskExecutor) {
|
|
||||||
this.redisTaskExecutor = redisTaskExecutor;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Autowired(required = false)
|
|
||||||
@Qualifier("springSessionRedisSubscriptionExecutor")
|
|
||||||
public void setRedisSubscriptionExecutor(Executor redisSubscriptionExecutor) {
|
|
||||||
this.redisSubscriptionExecutor = redisSubscriptionExecutor;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Autowired(required = false)
|
@Autowired(required = false)
|
||||||
public void setSessionRepositoryCustomizer(
|
public void setSessionRepositoryCustomizer(
|
||||||
ObjectProvider<SessionRepositoryCustomizer<RedisIndexedSessionRepository>> sessionRepositoryCustomizers) {
|
ObjectProvider<SessionRepositoryCustomizer<RedisSessionRepository>> sessionRepositoryCustomizers) {
|
||||||
this.sessionRepositoryCustomizers = sessionRepositoryCustomizers.orderedStream().collect(Collectors.toList());
|
this.sessionRepositoryCustomizers = sessionRepositoryCustomizers.orderedStream().collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,14 +170,10 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
|
|||||||
}
|
}
|
||||||
this.flushMode = flushMode;
|
this.flushMode = flushMode;
|
||||||
this.saveMode = attributes.getEnum("saveMode");
|
this.saveMode = attributes.getEnum("saveMode");
|
||||||
String cleanupCron = attributes.getString("cleanupCron");
|
|
||||||
if (StringUtils.hasText(cleanupCron)) {
|
|
||||||
this.cleanupCron = cleanupCron;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private RedisTemplate<Object, Object> createRedisTemplate() {
|
private RedisTemplate<String, Object> createRedisTemplate() {
|
||||||
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
|
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
|
||||||
redisTemplate.setKeySerializer(new StringRedisSerializer());
|
redisTemplate.setKeySerializer(new StringRedisSerializer());
|
||||||
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
|
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
|
||||||
if (this.defaultRedisSerializer != null) {
|
if (this.defaultRedisSerializer != null) {
|
||||||
@@ -292,77 +185,4 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
|
|||||||
return redisTemplate;
|
return redisTemplate;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int resolveDatabase() {
|
|
||||||
if (ClassUtils.isPresent("io.lettuce.core.RedisClient", null)
|
|
||||||
&& this.redisConnectionFactory instanceof LettuceConnectionFactory) {
|
|
||||||
return ((LettuceConnectionFactory) this.redisConnectionFactory).getDatabase();
|
|
||||||
}
|
|
||||||
if (ClassUtils.isPresent("redis.clients.jedis.Jedis", null)
|
|
||||||
&& this.redisConnectionFactory instanceof JedisConnectionFactory) {
|
|
||||||
return ((JedisConnectionFactory) this.redisConnectionFactory).getDatabase();
|
|
||||||
}
|
|
||||||
return RedisIndexedSessionRepository.DEFAULT_DATABASE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ensures that Redis is configured to send keyspace notifications. This is important
|
|
||||||
* to ensure that expiration and deletion of sessions trigger SessionDestroyedEvents.
|
|
||||||
* Without the SessionDestroyedEvent resources may not get cleaned up properly. For
|
|
||||||
* example, the mapping of the Session to WebSocket connections may not get cleaned
|
|
||||||
* up.
|
|
||||||
*/
|
|
||||||
static class EnableRedisKeyspaceNotificationsInitializer implements InitializingBean {
|
|
||||||
|
|
||||||
private final RedisConnectionFactory connectionFactory;
|
|
||||||
|
|
||||||
private ConfigureRedisAction configure;
|
|
||||||
|
|
||||||
EnableRedisKeyspaceNotificationsInitializer(RedisConnectionFactory connectionFactory,
|
|
||||||
ConfigureRedisAction configure) {
|
|
||||||
this.connectionFactory = connectionFactory;
|
|
||||||
this.configure = configure;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void afterPropertiesSet() {
|
|
||||||
if (this.configure == ConfigureRedisAction.NO_OP) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
RedisConnection connection = this.connectionFactory.getConnection();
|
|
||||||
try {
|
|
||||||
this.configure.configure(connection);
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
try {
|
|
||||||
connection.close();
|
|
||||||
}
|
|
||||||
catch (Exception ex) {
|
|
||||||
LogFactory.getLog(getClass()).error("Error closing RedisConnection", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configuration of scheduled job for cleaning up expired sessions.
|
|
||||||
*/
|
|
||||||
@EnableScheduling
|
|
||||||
@Configuration(proxyBeanMethods = false)
|
|
||||||
class SessionCleanupConfiguration implements SchedulingConfigurer {
|
|
||||||
|
|
||||||
private final RedisIndexedSessionRepository sessionRepository;
|
|
||||||
|
|
||||||
SessionCleanupConfiguration(RedisIndexedSessionRepository sessionRepository) {
|
|
||||||
this.sessionRepository = sessionRepository;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
|
|
||||||
taskRegistrar.addCronTask(this.sessionRepository::cleanupExpiredSessions,
|
|
||||||
RedisHttpSessionConfiguration.this.cleanupCron);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2014-2022 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.session.data.redis.config.annotation.web.http;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.ImportSelector;
|
||||||
|
import org.springframework.core.type.AnnotationMetadata;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dynamically determines which session repository configuration to include using the
|
||||||
|
* {@link EnableRedisHttpSession} annotation.
|
||||||
|
*
|
||||||
|
* @author Eleftheria Stein
|
||||||
|
* @since 3.0
|
||||||
|
*/
|
||||||
|
final class RedisHttpSessionConfigurationSelector implements ImportSelector {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] selectImports(AnnotationMetadata importMetadata) {
|
||||||
|
if (!importMetadata.hasAnnotation(EnableRedisHttpSession.class.getName())) {
|
||||||
|
return new String[0];
|
||||||
|
}
|
||||||
|
EnableRedisHttpSession annotation = importMetadata.getAnnotations().get(EnableRedisHttpSession.class)
|
||||||
|
.synthesize();
|
||||||
|
if (annotation.enableIndexingAndEvents()) {
|
||||||
|
return new String[] { RedisIndexedHttpSessionConfiguration.class.getName() };
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return new String[] { RedisHttpSessionConfiguration.class.getName() };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,361 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2014-2022 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.session.data.redis.config.annotation.web.http;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||||
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
|
import org.springframework.beans.factory.ObjectProvider;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
|
import org.springframework.context.ApplicationEventPublisher;
|
||||||
|
import org.springframework.context.EmbeddedValueResolverAware;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.ImportAware;
|
||||||
|
import org.springframework.core.annotation.AnnotationAttributes;
|
||||||
|
import org.springframework.core.type.AnnotationMetadata;
|
||||||
|
import org.springframework.data.redis.connection.RedisConnection;
|
||||||
|
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||||
|
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
|
||||||
|
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
|
||||||
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
|
import org.springframework.data.redis.listener.ChannelTopic;
|
||||||
|
import org.springframework.data.redis.listener.PatternTopic;
|
||||||
|
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
|
||||||
|
import org.springframework.data.redis.serializer.RedisSerializer;
|
||||||
|
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||||
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
|
import org.springframework.scheduling.annotation.SchedulingConfigurer;
|
||||||
|
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
|
||||||
|
import org.springframework.session.FlushMode;
|
||||||
|
import org.springframework.session.IndexResolver;
|
||||||
|
import org.springframework.session.MapSession;
|
||||||
|
import org.springframework.session.SaveMode;
|
||||||
|
import org.springframework.session.Session;
|
||||||
|
import org.springframework.session.config.SessionRepositoryCustomizer;
|
||||||
|
import org.springframework.session.config.annotation.web.http.SpringHttpSessionConfiguration;
|
||||||
|
import org.springframework.session.data.redis.RedisFlushMode;
|
||||||
|
import org.springframework.session.data.redis.RedisIndexedSessionRepository;
|
||||||
|
import org.springframework.session.data.redis.config.ConfigureNotifyKeyspaceEventsAction;
|
||||||
|
import org.springframework.session.data.redis.config.ConfigureRedisAction;
|
||||||
|
import org.springframework.session.data.redis.config.annotation.SpringSessionRedisConnectionFactory;
|
||||||
|
import org.springframework.session.web.http.SessionRepositoryFilter;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.util.ClassUtils;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
import org.springframework.util.StringValueResolver;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exposes the {@link SessionRepositoryFilter} as a bean named
|
||||||
|
* {@code springSessionRepositoryFilter}. In order to use this a single
|
||||||
|
* {@link RedisConnectionFactory} must be exposed as a Bean.
|
||||||
|
*
|
||||||
|
* @author Eleftheria Stein
|
||||||
|
* @since 3.0
|
||||||
|
*/
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
public class RedisIndexedHttpSessionConfiguration extends SpringHttpSessionConfiguration
|
||||||
|
implements BeanClassLoaderAware, EmbeddedValueResolverAware, ImportAware {
|
||||||
|
|
||||||
|
static final String DEFAULT_CLEANUP_CRON = "0 * * * * *";
|
||||||
|
|
||||||
|
private Integer maxInactiveIntervalInSeconds = MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS;
|
||||||
|
|
||||||
|
private String redisNamespace = RedisIndexedSessionRepository.DEFAULT_NAMESPACE;
|
||||||
|
|
||||||
|
private FlushMode flushMode = FlushMode.ON_SAVE;
|
||||||
|
|
||||||
|
private SaveMode saveMode = SaveMode.ON_SET_ATTRIBUTE;
|
||||||
|
|
||||||
|
private String cleanupCron = DEFAULT_CLEANUP_CRON;
|
||||||
|
|
||||||
|
private ConfigureRedisAction configureRedisAction = new ConfigureNotifyKeyspaceEventsAction();
|
||||||
|
|
||||||
|
private RedisConnectionFactory redisConnectionFactory;
|
||||||
|
|
||||||
|
private IndexResolver<Session> indexResolver;
|
||||||
|
|
||||||
|
private RedisSerializer<Object> defaultRedisSerializer;
|
||||||
|
|
||||||
|
private ApplicationEventPublisher applicationEventPublisher;
|
||||||
|
|
||||||
|
private Executor redisTaskExecutor;
|
||||||
|
|
||||||
|
private Executor redisSubscriptionExecutor;
|
||||||
|
|
||||||
|
private List<SessionRepositoryCustomizer<RedisIndexedSessionRepository>> sessionRepositoryCustomizers;
|
||||||
|
|
||||||
|
private ClassLoader classLoader;
|
||||||
|
|
||||||
|
private StringValueResolver embeddedValueResolver;
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public RedisIndexedSessionRepository sessionRepository() {
|
||||||
|
RedisTemplate<Object, Object> redisTemplate = createRedisTemplate();
|
||||||
|
RedisIndexedSessionRepository sessionRepository = new RedisIndexedSessionRepository(redisTemplate);
|
||||||
|
sessionRepository.setApplicationEventPublisher(this.applicationEventPublisher);
|
||||||
|
if (this.indexResolver != null) {
|
||||||
|
sessionRepository.setIndexResolver(this.indexResolver);
|
||||||
|
}
|
||||||
|
if (this.defaultRedisSerializer != null) {
|
||||||
|
sessionRepository.setDefaultSerializer(this.defaultRedisSerializer);
|
||||||
|
}
|
||||||
|
sessionRepository.setDefaultMaxInactiveInterval(this.maxInactiveIntervalInSeconds);
|
||||||
|
if (StringUtils.hasText(this.redisNamespace)) {
|
||||||
|
sessionRepository.setRedisKeyNamespace(this.redisNamespace);
|
||||||
|
}
|
||||||
|
sessionRepository.setFlushMode(this.flushMode);
|
||||||
|
sessionRepository.setSaveMode(this.saveMode);
|
||||||
|
int database = resolveDatabase();
|
||||||
|
sessionRepository.setDatabase(database);
|
||||||
|
this.sessionRepositoryCustomizers
|
||||||
|
.forEach((sessionRepositoryCustomizer) -> sessionRepositoryCustomizer.customize(sessionRepository));
|
||||||
|
return sessionRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public RedisMessageListenerContainer springSessionRedisMessageListenerContainer(
|
||||||
|
RedisIndexedSessionRepository sessionRepository) {
|
||||||
|
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
|
||||||
|
container.setConnectionFactory(this.redisConnectionFactory);
|
||||||
|
if (this.redisTaskExecutor != null) {
|
||||||
|
container.setTaskExecutor(this.redisTaskExecutor);
|
||||||
|
}
|
||||||
|
if (this.redisSubscriptionExecutor != null) {
|
||||||
|
container.setSubscriptionExecutor(this.redisSubscriptionExecutor);
|
||||||
|
}
|
||||||
|
container.addMessageListener(sessionRepository,
|
||||||
|
Arrays.asList(new ChannelTopic(sessionRepository.getSessionDeletedChannel()),
|
||||||
|
new ChannelTopic(sessionRepository.getSessionExpiredChannel())));
|
||||||
|
container.addMessageListener(sessionRepository,
|
||||||
|
Collections.singletonList(new PatternTopic(sessionRepository.getSessionCreatedChannelPrefix() + "*")));
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public InitializingBean enableRedisKeyspaceNotificationsInitializer() {
|
||||||
|
return new EnableRedisKeyspaceNotificationsInitializer(this.redisConnectionFactory, this.configureRedisAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMaxInactiveIntervalInSeconds(int maxInactiveIntervalInSeconds) {
|
||||||
|
this.maxInactiveIntervalInSeconds = maxInactiveIntervalInSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRedisNamespace(String namespace) {
|
||||||
|
this.redisNamespace = namespace;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public void setRedisFlushMode(RedisFlushMode redisFlushMode) {
|
||||||
|
Assert.notNull(redisFlushMode, "redisFlushMode cannot be null");
|
||||||
|
setFlushMode(redisFlushMode.getFlushMode());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFlushMode(FlushMode flushMode) {
|
||||||
|
Assert.notNull(flushMode, "flushMode cannot be null");
|
||||||
|
this.flushMode = flushMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSaveMode(SaveMode saveMode) {
|
||||||
|
this.saveMode = saveMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCleanupCron(String cleanupCron) {
|
||||||
|
this.cleanupCron = cleanupCron;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the action to perform for configuring Redis.
|
||||||
|
* @param configureRedisAction the configureRedis to set. The default is
|
||||||
|
* {@link ConfigureNotifyKeyspaceEventsAction}.
|
||||||
|
*/
|
||||||
|
@Autowired(required = false)
|
||||||
|
public void setConfigureRedisAction(ConfigureRedisAction configureRedisAction) {
|
||||||
|
this.configureRedisAction = configureRedisAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public void setRedisConnectionFactory(
|
||||||
|
@SpringSessionRedisConnectionFactory ObjectProvider<RedisConnectionFactory> springSessionRedisConnectionFactory,
|
||||||
|
ObjectProvider<RedisConnectionFactory> redisConnectionFactory) {
|
||||||
|
RedisConnectionFactory redisConnectionFactoryToUse = springSessionRedisConnectionFactory.getIfAvailable();
|
||||||
|
if (redisConnectionFactoryToUse == null) {
|
||||||
|
redisConnectionFactoryToUse = redisConnectionFactory.getObject();
|
||||||
|
}
|
||||||
|
this.redisConnectionFactory = redisConnectionFactoryToUse;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Autowired(required = false)
|
||||||
|
@Qualifier("springSessionDefaultRedisSerializer")
|
||||||
|
public void setDefaultRedisSerializer(RedisSerializer<Object> defaultRedisSerializer) {
|
||||||
|
this.defaultRedisSerializer = defaultRedisSerializer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
|
||||||
|
this.applicationEventPublisher = applicationEventPublisher;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Autowired(required = false)
|
||||||
|
public void setIndexResolver(IndexResolver<Session> indexResolver) {
|
||||||
|
this.indexResolver = indexResolver;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Autowired(required = false)
|
||||||
|
@Qualifier("springSessionRedisTaskExecutor")
|
||||||
|
public void setRedisTaskExecutor(Executor redisTaskExecutor) {
|
||||||
|
this.redisTaskExecutor = redisTaskExecutor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Autowired(required = false)
|
||||||
|
@Qualifier("springSessionRedisSubscriptionExecutor")
|
||||||
|
public void setRedisSubscriptionExecutor(Executor redisSubscriptionExecutor) {
|
||||||
|
this.redisSubscriptionExecutor = redisSubscriptionExecutor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Autowired(required = false)
|
||||||
|
public void setSessionRepositoryCustomizer(
|
||||||
|
ObjectProvider<SessionRepositoryCustomizer<RedisIndexedSessionRepository>> sessionRepositoryCustomizers) {
|
||||||
|
this.sessionRepositoryCustomizers = sessionRepositoryCustomizers.orderedStream().collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||||
|
this.classLoader = classLoader;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setEmbeddedValueResolver(StringValueResolver resolver) {
|
||||||
|
this.embeddedValueResolver = resolver;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public void setImportMetadata(AnnotationMetadata importMetadata) {
|
||||||
|
Map<String, Object> attributeMap = importMetadata
|
||||||
|
.getAnnotationAttributes(EnableRedisHttpSession.class.getName());
|
||||||
|
AnnotationAttributes attributes = AnnotationAttributes.fromMap(attributeMap);
|
||||||
|
this.maxInactiveIntervalInSeconds = attributes.getNumber("maxInactiveIntervalInSeconds");
|
||||||
|
String redisNamespaceValue = attributes.getString("redisNamespace");
|
||||||
|
if (StringUtils.hasText(redisNamespaceValue)) {
|
||||||
|
this.redisNamespace = this.embeddedValueResolver.resolveStringValue(redisNamespaceValue);
|
||||||
|
}
|
||||||
|
FlushMode flushMode = attributes.getEnum("flushMode");
|
||||||
|
RedisFlushMode redisFlushMode = attributes.getEnum("redisFlushMode");
|
||||||
|
if (flushMode == FlushMode.ON_SAVE && redisFlushMode != RedisFlushMode.ON_SAVE) {
|
||||||
|
flushMode = redisFlushMode.getFlushMode();
|
||||||
|
}
|
||||||
|
this.flushMode = flushMode;
|
||||||
|
this.saveMode = attributes.getEnum("saveMode");
|
||||||
|
}
|
||||||
|
|
||||||
|
private RedisTemplate<Object, Object> createRedisTemplate() {
|
||||||
|
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
|
||||||
|
redisTemplate.setKeySerializer(new StringRedisSerializer());
|
||||||
|
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
|
||||||
|
if (this.defaultRedisSerializer != null) {
|
||||||
|
redisTemplate.setDefaultSerializer(this.defaultRedisSerializer);
|
||||||
|
}
|
||||||
|
redisTemplate.setConnectionFactory(this.redisConnectionFactory);
|
||||||
|
redisTemplate.setBeanClassLoader(this.classLoader);
|
||||||
|
redisTemplate.afterPropertiesSet();
|
||||||
|
return redisTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int resolveDatabase() {
|
||||||
|
if (ClassUtils.isPresent("io.lettuce.core.RedisClient", null)
|
||||||
|
&& this.redisConnectionFactory instanceof LettuceConnectionFactory) {
|
||||||
|
return ((LettuceConnectionFactory) this.redisConnectionFactory).getDatabase();
|
||||||
|
}
|
||||||
|
if (ClassUtils.isPresent("redis.clients.jedis.Jedis", null)
|
||||||
|
&& this.redisConnectionFactory instanceof JedisConnectionFactory) {
|
||||||
|
return ((JedisConnectionFactory) this.redisConnectionFactory).getDatabase();
|
||||||
|
}
|
||||||
|
return RedisIndexedSessionRepository.DEFAULT_DATABASE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures that Redis is configured to send keyspace notifications. This is important
|
||||||
|
* to ensure that expiration and deletion of sessions trigger SessionDestroyedEvents.
|
||||||
|
* Without the SessionDestroyedEvent resources may not get cleaned up properly. For
|
||||||
|
* example, the mapping of the Session to WebSocket connections may not get cleaned
|
||||||
|
* up.
|
||||||
|
*/
|
||||||
|
static class EnableRedisKeyspaceNotificationsInitializer implements InitializingBean {
|
||||||
|
|
||||||
|
private final RedisConnectionFactory connectionFactory;
|
||||||
|
|
||||||
|
private ConfigureRedisAction configure;
|
||||||
|
|
||||||
|
EnableRedisKeyspaceNotificationsInitializer(RedisConnectionFactory connectionFactory,
|
||||||
|
ConfigureRedisAction configure) {
|
||||||
|
this.connectionFactory = connectionFactory;
|
||||||
|
this.configure = configure;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterPropertiesSet() {
|
||||||
|
if (this.configure == ConfigureRedisAction.NO_OP) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
RedisConnection connection = this.connectionFactory.getConnection();
|
||||||
|
try {
|
||||||
|
this.configure.configure(connection);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
try {
|
||||||
|
connection.close();
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
LogFactory.getLog(getClass()).error("Error closing RedisConnection", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration of scheduled job for cleaning up expired sessions.
|
||||||
|
*/
|
||||||
|
@EnableScheduling
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
class SessionCleanupConfiguration implements SchedulingConfigurer {
|
||||||
|
|
||||||
|
private final RedisIndexedSessionRepository sessionRepository;
|
||||||
|
|
||||||
|
SessionCleanupConfiguration(RedisIndexedSessionRepository sessionRepository) {
|
||||||
|
this.sessionRepository = sessionRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
|
||||||
|
taskRegistrar.addCronTask(this.sessionRepository::cleanupExpiredSessions,
|
||||||
|
RedisIndexedHttpSessionConfiguration.this.cleanupCron);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2014-2019 the original author or authors.
|
* Copyright 2014-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -49,14 +49,14 @@ class EnableRedisKeyspaceNotificationsInitializerTests {
|
|||||||
@Captor
|
@Captor
|
||||||
ArgumentCaptor<String> options;
|
ArgumentCaptor<String> options;
|
||||||
|
|
||||||
private RedisHttpSessionConfiguration.EnableRedisKeyspaceNotificationsInitializer initializer;
|
private RedisIndexedHttpSessionConfiguration.EnableRedisKeyspaceNotificationsInitializer initializer;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setup() {
|
void setup() {
|
||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
given(this.connectionFactory.getConnection()).willReturn(this.connection);
|
given(this.connectionFactory.getConnection()).willReturn(this.connection);
|
||||||
|
|
||||||
this.initializer = new RedisHttpSessionConfiguration.EnableRedisKeyspaceNotificationsInitializer(
|
this.initializer = new RedisIndexedHttpSessionConfiguration.EnableRedisKeyspaceNotificationsInitializer(
|
||||||
this.connectionFactory, new ConfigureNotifyKeyspaceEventsAction());
|
this.connectionFactory, new ConfigureNotifyKeyspaceEventsAction());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
package org.springframework.session.data.redis.config.annotation.web.http;
|
package org.springframework.session.data.redis.config.annotation.web.http;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.time.Duration;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
@@ -34,15 +34,12 @@ import org.springframework.data.redis.connection.RedisConnection;
|
|||||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||||
import org.springframework.data.redis.connection.SubscriptionListener;
|
import org.springframework.data.redis.connection.SubscriptionListener;
|
||||||
import org.springframework.data.redis.core.RedisOperations;
|
import org.springframework.data.redis.core.RedisOperations;
|
||||||
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
|
|
||||||
import org.springframework.mock.env.MockEnvironment;
|
import org.springframework.mock.env.MockEnvironment;
|
||||||
import org.springframework.session.FlushMode;
|
import org.springframework.session.FlushMode;
|
||||||
import org.springframework.session.IndexResolver;
|
|
||||||
import org.springframework.session.SaveMode;
|
import org.springframework.session.SaveMode;
|
||||||
import org.springframework.session.Session;
|
|
||||||
import org.springframework.session.config.SessionRepositoryCustomizer;
|
import org.springframework.session.config.SessionRepositoryCustomizer;
|
||||||
import org.springframework.session.data.redis.RedisFlushMode;
|
import org.springframework.session.data.redis.RedisFlushMode;
|
||||||
import org.springframework.session.data.redis.RedisIndexedSessionRepository;
|
import org.springframework.session.data.redis.RedisSessionRepository;
|
||||||
import org.springframework.session.data.redis.config.annotation.SpringSessionRedisConnectionFactory;
|
import org.springframework.session.data.redis.config.annotation.SpringSessionRedisConnectionFactory;
|
||||||
import org.springframework.test.util.ReflectionTestUtils;
|
import org.springframework.test.util.ReflectionTestUtils;
|
||||||
|
|
||||||
@@ -62,9 +59,7 @@ import static org.mockito.Mockito.mock;
|
|||||||
*/
|
*/
|
||||||
class RedisHttpSessionConfigurationTests {
|
class RedisHttpSessionConfigurationTests {
|
||||||
|
|
||||||
private static final int MAX_INACTIVE_INTERVAL_IN_SECONDS = 600;
|
private static final Duration MAX_INACTIVE_INTERVAL_DURATION = Duration.ofSeconds(600);
|
||||||
|
|
||||||
private static final String CLEANUP_CRON_EXPRESSION = "0 0 * * * *";
|
|
||||||
|
|
||||||
private AnnotationConfigApplicationContext context;
|
private AnnotationConfigApplicationContext context;
|
||||||
|
|
||||||
@@ -100,7 +95,7 @@ class RedisHttpSessionConfigurationTests {
|
|||||||
@Test
|
@Test
|
||||||
void customFlushImmediately() {
|
void customFlushImmediately() {
|
||||||
registerAndRefresh(RedisConfig.class, CustomFlushImmediatelyConfiguration.class);
|
registerAndRefresh(RedisConfig.class, CustomFlushImmediatelyConfiguration.class);
|
||||||
RedisIndexedSessionRepository sessionRepository = this.context.getBean(RedisIndexedSessionRepository.class);
|
RedisSessionRepository sessionRepository = this.context.getBean(RedisSessionRepository.class);
|
||||||
assertThat(sessionRepository).isNotNull();
|
assertThat(sessionRepository).isNotNull();
|
||||||
assertThat(ReflectionTestUtils.getField(sessionRepository, "flushMode")).isEqualTo(FlushMode.IMMEDIATE);
|
assertThat(ReflectionTestUtils.getField(sessionRepository, "flushMode")).isEqualTo(FlushMode.IMMEDIATE);
|
||||||
}
|
}
|
||||||
@@ -108,7 +103,7 @@ class RedisHttpSessionConfigurationTests {
|
|||||||
@Test
|
@Test
|
||||||
void customFlushImmediatelyLegacy() {
|
void customFlushImmediatelyLegacy() {
|
||||||
registerAndRefresh(RedisConfig.class, CustomFlushImmediatelyLegacyConfiguration.class);
|
registerAndRefresh(RedisConfig.class, CustomFlushImmediatelyLegacyConfiguration.class);
|
||||||
RedisIndexedSessionRepository sessionRepository = this.context.getBean(RedisIndexedSessionRepository.class);
|
RedisSessionRepository sessionRepository = this.context.getBean(RedisSessionRepository.class);
|
||||||
assertThat(sessionRepository).isNotNull();
|
assertThat(sessionRepository).isNotNull();
|
||||||
assertThat(ReflectionTestUtils.getField(sessionRepository, "flushMode")).isEqualTo(FlushMode.IMMEDIATE);
|
assertThat(ReflectionTestUtils.getField(sessionRepository, "flushMode")).isEqualTo(FlushMode.IMMEDIATE);
|
||||||
}
|
}
|
||||||
@@ -116,7 +111,7 @@ class RedisHttpSessionConfigurationTests {
|
|||||||
@Test
|
@Test
|
||||||
void setCustomFlushImmediately() {
|
void setCustomFlushImmediately() {
|
||||||
registerAndRefresh(RedisConfig.class, CustomFlushImmediatelySetConfiguration.class);
|
registerAndRefresh(RedisConfig.class, CustomFlushImmediatelySetConfiguration.class);
|
||||||
RedisIndexedSessionRepository sessionRepository = this.context.getBean(RedisIndexedSessionRepository.class);
|
RedisSessionRepository sessionRepository = this.context.getBean(RedisSessionRepository.class);
|
||||||
assertThat(sessionRepository).isNotNull();
|
assertThat(sessionRepository).isNotNull();
|
||||||
assertThat(ReflectionTestUtils.getField(sessionRepository, "flushMode")).isEqualTo(FlushMode.IMMEDIATE);
|
assertThat(ReflectionTestUtils.getField(sessionRepository, "flushMode")).isEqualTo(FlushMode.IMMEDIATE);
|
||||||
}
|
}
|
||||||
@@ -124,40 +119,22 @@ class RedisHttpSessionConfigurationTests {
|
|||||||
@Test
|
@Test
|
||||||
void setCustomFlushImmediatelyLegacy() {
|
void setCustomFlushImmediatelyLegacy() {
|
||||||
registerAndRefresh(RedisConfig.class, CustomFlushImmediatelySetLegacyConfiguration.class);
|
registerAndRefresh(RedisConfig.class, CustomFlushImmediatelySetLegacyConfiguration.class);
|
||||||
RedisIndexedSessionRepository sessionRepository = this.context.getBean(RedisIndexedSessionRepository.class);
|
RedisSessionRepository sessionRepository = this.context.getBean(RedisSessionRepository.class);
|
||||||
assertThat(sessionRepository).isNotNull();
|
assertThat(sessionRepository).isNotNull();
|
||||||
assertThat(ReflectionTestUtils.getField(sessionRepository, "flushMode")).isEqualTo(FlushMode.IMMEDIATE);
|
assertThat(ReflectionTestUtils.getField(sessionRepository, "flushMode")).isEqualTo(FlushMode.IMMEDIATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
void customCleanupCronAnnotation() {
|
|
||||||
registerAndRefresh(RedisConfig.class, CustomCleanupCronExpressionAnnotationConfiguration.class);
|
|
||||||
|
|
||||||
RedisHttpSessionConfiguration configuration = this.context.getBean(RedisHttpSessionConfiguration.class);
|
|
||||||
assertThat(configuration).isNotNull();
|
|
||||||
assertThat(ReflectionTestUtils.getField(configuration, "cleanupCron")).isEqualTo(CLEANUP_CRON_EXPRESSION);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void customCleanupCronSetter() {
|
|
||||||
registerAndRefresh(RedisConfig.class, CustomCleanupCronExpressionSetterConfiguration.class);
|
|
||||||
|
|
||||||
RedisHttpSessionConfiguration configuration = this.context.getBean(RedisHttpSessionConfiguration.class);
|
|
||||||
assertThat(configuration).isNotNull();
|
|
||||||
assertThat(ReflectionTestUtils.getField(configuration, "cleanupCron")).isEqualTo(CLEANUP_CRON_EXPRESSION);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void customSaveModeAnnotation() {
|
void customSaveModeAnnotation() {
|
||||||
registerAndRefresh(RedisConfig.class, CustomSaveModeExpressionAnnotationConfiguration.class);
|
registerAndRefresh(RedisConfig.class, CustomSaveModeExpressionAnnotationConfiguration.class);
|
||||||
assertThat(this.context.getBean(RedisIndexedSessionRepository.class)).hasFieldOrPropertyWithValue("saveMode",
|
assertThat(this.context.getBean(RedisSessionRepository.class)).hasFieldOrPropertyWithValue("saveMode",
|
||||||
SaveMode.ALWAYS);
|
SaveMode.ALWAYS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void customSaveModeSetter() {
|
void customSaveModeSetter() {
|
||||||
registerAndRefresh(RedisConfig.class, CustomSaveModeExpressionSetterConfiguration.class);
|
registerAndRefresh(RedisConfig.class, CustomSaveModeExpressionSetterConfiguration.class);
|
||||||
assertThat(this.context.getBean(RedisIndexedSessionRepository.class)).hasFieldOrPropertyWithValue("saveMode",
|
assertThat(this.context.getBean(RedisSessionRepository.class)).hasFieldOrPropertyWithValue("saveMode",
|
||||||
SaveMode.ALWAYS);
|
SaveMode.ALWAYS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,7 +142,7 @@ class RedisHttpSessionConfigurationTests {
|
|||||||
void qualifiedConnectionFactoryRedisConfig() {
|
void qualifiedConnectionFactoryRedisConfig() {
|
||||||
registerAndRefresh(RedisConfig.class, QualifiedConnectionFactoryRedisConfig.class);
|
registerAndRefresh(RedisConfig.class, QualifiedConnectionFactoryRedisConfig.class);
|
||||||
|
|
||||||
RedisIndexedSessionRepository repository = this.context.getBean(RedisIndexedSessionRepository.class);
|
RedisSessionRepository repository = this.context.getBean(RedisSessionRepository.class);
|
||||||
RedisConnectionFactory redisConnectionFactory = this.context.getBean("qualifiedRedisConnectionFactory",
|
RedisConnectionFactory redisConnectionFactory = this.context.getBean("qualifiedRedisConnectionFactory",
|
||||||
RedisConnectionFactory.class);
|
RedisConnectionFactory.class);
|
||||||
assertThat(repository).isNotNull();
|
assertThat(repository).isNotNull();
|
||||||
@@ -181,7 +158,7 @@ class RedisHttpSessionConfigurationTests {
|
|||||||
void primaryConnectionFactoryRedisConfig() {
|
void primaryConnectionFactoryRedisConfig() {
|
||||||
registerAndRefresh(RedisConfig.class, PrimaryConnectionFactoryRedisConfig.class);
|
registerAndRefresh(RedisConfig.class, PrimaryConnectionFactoryRedisConfig.class);
|
||||||
|
|
||||||
RedisIndexedSessionRepository repository = this.context.getBean(RedisIndexedSessionRepository.class);
|
RedisSessionRepository repository = this.context.getBean(RedisSessionRepository.class);
|
||||||
RedisConnectionFactory redisConnectionFactory = this.context.getBean("primaryRedisConnectionFactory",
|
RedisConnectionFactory redisConnectionFactory = this.context.getBean("primaryRedisConnectionFactory",
|
||||||
RedisConnectionFactory.class);
|
RedisConnectionFactory.class);
|
||||||
assertThat(repository).isNotNull();
|
assertThat(repository).isNotNull();
|
||||||
@@ -197,7 +174,7 @@ class RedisHttpSessionConfigurationTests {
|
|||||||
void qualifiedAndPrimaryConnectionFactoryRedisConfig() {
|
void qualifiedAndPrimaryConnectionFactoryRedisConfig() {
|
||||||
registerAndRefresh(RedisConfig.class, QualifiedAndPrimaryConnectionFactoryRedisConfig.class);
|
registerAndRefresh(RedisConfig.class, QualifiedAndPrimaryConnectionFactoryRedisConfig.class);
|
||||||
|
|
||||||
RedisIndexedSessionRepository repository = this.context.getBean(RedisIndexedSessionRepository.class);
|
RedisSessionRepository repository = this.context.getBean(RedisSessionRepository.class);
|
||||||
RedisConnectionFactory redisConnectionFactory = this.context.getBean("qualifiedRedisConnectionFactory",
|
RedisConnectionFactory redisConnectionFactory = this.context.getBean("qualifiedRedisConnectionFactory",
|
||||||
RedisConnectionFactory.class);
|
RedisConnectionFactory.class);
|
||||||
assertThat(repository).isNotNull();
|
assertThat(repository).isNotNull();
|
||||||
@@ -213,7 +190,7 @@ class RedisHttpSessionConfigurationTests {
|
|||||||
void namedConnectionFactoryRedisConfig() {
|
void namedConnectionFactoryRedisConfig() {
|
||||||
registerAndRefresh(RedisConfig.class, NamedConnectionFactoryRedisConfig.class);
|
registerAndRefresh(RedisConfig.class, NamedConnectionFactoryRedisConfig.class);
|
||||||
|
|
||||||
RedisIndexedSessionRepository repository = this.context.getBean(RedisIndexedSessionRepository.class);
|
RedisSessionRepository repository = this.context.getBean(RedisSessionRepository.class);
|
||||||
RedisConnectionFactory redisConnectionFactory = this.context.getBean("redisConnectionFactory",
|
RedisConnectionFactory redisConnectionFactory = this.context.getBean("redisConnectionFactory",
|
||||||
RedisConnectionFactory.class);
|
RedisConnectionFactory.class);
|
||||||
assertThat(repository).isNotNull();
|
assertThat(repository).isNotNull();
|
||||||
@@ -232,32 +209,12 @@ class RedisHttpSessionConfigurationTests {
|
|||||||
.withMessageContaining("expected single matching bean but found 2");
|
.withMessageContaining("expected single matching bean but found 2");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
void customIndexResolverConfiguration() {
|
|
||||||
registerAndRefresh(RedisConfig.class, CustomIndexResolverConfiguration.class);
|
|
||||||
RedisIndexedSessionRepository repository = this.context.getBean(RedisIndexedSessionRepository.class);
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
IndexResolver<Session> indexResolver = this.context.getBean(IndexResolver.class);
|
|
||||||
assertThat(repository).isNotNull();
|
|
||||||
assertThat(indexResolver).isNotNull();
|
|
||||||
assertThat(repository).hasFieldOrPropertyWithValue("indexResolver", indexResolver);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test // gh-1252
|
|
||||||
void customRedisMessageListenerContainerConfig() {
|
|
||||||
registerAndRefresh(RedisConfig.class, CustomRedisMessageListenerContainerConfig.class);
|
|
||||||
Map<String, RedisMessageListenerContainer> beans = this.context
|
|
||||||
.getBeansOfType(RedisMessageListenerContainer.class);
|
|
||||||
assertThat(beans).hasSize(2);
|
|
||||||
assertThat(beans).containsKeys("springSessionRedisMessageListenerContainer", "redisMessageListenerContainer");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void sessionRepositoryCustomizer() {
|
void sessionRepositoryCustomizer() {
|
||||||
registerAndRefresh(RedisConfig.class, SessionRepositoryCustomizerConfiguration.class);
|
registerAndRefresh(RedisConfig.class, SessionRepositoryCustomizerConfiguration.class);
|
||||||
RedisIndexedSessionRepository sessionRepository = this.context.getBean(RedisIndexedSessionRepository.class);
|
RedisSessionRepository sessionRepository = this.context.getBean(RedisSessionRepository.class);
|
||||||
assertThat(sessionRepository).hasFieldOrPropertyWithValue("defaultMaxInactiveInterval",
|
assertThat(sessionRepository).hasFieldOrPropertyWithValue("defaultMaxInactiveInterval",
|
||||||
MAX_INACTIVE_INTERVAL_IN_SECONDS);
|
MAX_INACTIVE_INTERVAL_DURATION);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void registerAndRefresh(Class<?>... annotatedClasses) {
|
private void registerAndRefresh(Class<?>... annotatedClasses) {
|
||||||
@@ -338,20 +295,6 @@ class RedisHttpSessionConfigurationTests {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@EnableRedisHttpSession(cleanupCron = CLEANUP_CRON_EXPRESSION)
|
|
||||||
static class CustomCleanupCronExpressionAnnotationConfiguration {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Configuration
|
|
||||||
static class CustomCleanupCronExpressionSetterConfiguration extends RedisHttpSessionConfiguration {
|
|
||||||
|
|
||||||
CustomCleanupCronExpressionSetterConfiguration() {
|
|
||||||
setCleanupCron(CLEANUP_CRON_EXPRESSION);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@EnableRedisHttpSession(saveMode = SaveMode.ALWAYS)
|
@EnableRedisHttpSession(saveMode = SaveMode.ALWAYS)
|
||||||
static class CustomSaveModeExpressionAnnotationConfiguration {
|
static class CustomSaveModeExpressionAnnotationConfiguration {
|
||||||
|
|
||||||
@@ -442,43 +385,20 @@ class RedisHttpSessionConfigurationTests {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
|
||||||
@EnableRedisHttpSession
|
|
||||||
static class CustomIndexResolverConfiguration {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
IndexResolver<Session> indexResolver() {
|
|
||||||
return mock(IndexResolver.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Configuration
|
|
||||||
@EnableRedisHttpSession
|
|
||||||
static class CustomRedisMessageListenerContainerConfig {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
RedisMessageListenerContainer redisMessageListenerContainer() {
|
|
||||||
return mock(RedisMessageListenerContainer.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@EnableRedisHttpSession
|
@EnableRedisHttpSession
|
||||||
static class SessionRepositoryCustomizerConfiguration {
|
static class SessionRepositoryCustomizerConfiguration {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@Order(0)
|
@Order(0)
|
||||||
SessionRepositoryCustomizer<RedisIndexedSessionRepository> sessionRepositoryCustomizerOne() {
|
SessionRepositoryCustomizer<RedisSessionRepository> sessionRepositoryCustomizerOne() {
|
||||||
return (sessionRepository) -> sessionRepository.setDefaultMaxInactiveInterval(0);
|
return (sessionRepository) -> sessionRepository.setDefaultMaxInactiveInterval(Duration.ZERO);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@Order(1)
|
@Order(1)
|
||||||
SessionRepositoryCustomizer<RedisIndexedSessionRepository> sessionRepositoryCustomizerTwo() {
|
SessionRepositoryCustomizer<RedisSessionRepository> sessionRepositoryCustomizerTwo() {
|
||||||
return (sessionRepository) -> sessionRepository
|
return (sessionRepository) -> sessionRepository
|
||||||
.setDefaultMaxInactiveInterval(MAX_INACTIVE_INTERVAL_IN_SECONDS);
|
.setDefaultMaxInactiveInterval(MAX_INACTIVE_INTERVAL_DURATION);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2014-2019 the original author or authors.
|
* Copyright 2014-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -24,7 +24,7 @@ import org.mockito.MockitoAnnotations;
|
|||||||
import org.springframework.data.redis.connection.RedisConnection;
|
import org.springframework.data.redis.connection.RedisConnection;
|
||||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||||
import org.springframework.session.data.redis.config.ConfigureRedisAction;
|
import org.springframework.session.data.redis.config.ConfigureRedisAction;
|
||||||
import org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration.EnableRedisKeyspaceNotificationsInitializer;
|
import org.springframework.session.data.redis.config.annotation.web.http.RedisIndexedHttpSessionConfiguration.EnableRedisKeyspaceNotificationsInitializer;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown;
|
import static org.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown;
|
||||||
import static org.mockito.BDDMockito.given;
|
import static org.mockito.BDDMockito.given;
|
||||||
@@ -33,7 +33,7 @@ import static org.mockito.Mockito.mock;
|
|||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||||
|
|
||||||
class RedisHttpSessionConfigurationMockTests {
|
class RedisIndexedHttpSessionConfigurationMockTests {
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
RedisConnectionFactory factory;
|
RedisConnectionFactory factory;
|
||||||
@@ -48,7 +48,7 @@ import static org.mockito.Mockito.verify;
|
|||||||
@ExtendWith(SpringExtension.class)
|
@ExtendWith(SpringExtension.class)
|
||||||
@ContextConfiguration
|
@ContextConfiguration
|
||||||
@WebAppConfiguration
|
@WebAppConfiguration
|
||||||
class RedisHttpSessionConfigurationOverrideSessionTaskExecutor {
|
class RedisIndexedHttpSessionConfigurationOverrideSessionTaskExecutor {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
RedisMessageListenerContainer redisMessageListenerContainer;
|
RedisMessageListenerContainer redisMessageListenerContainer;
|
||||||
@@ -61,7 +61,7 @@ class RedisHttpSessionConfigurationOverrideSessionTaskExecutor {
|
|||||||
verify(this.springSessionRedisTaskExecutor, times(1)).execute(any(Runnable.class));
|
verify(this.springSessionRedisTaskExecutor, times(1)).execute(any(Runnable.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@EnableRedisHttpSession
|
@EnableRedisHttpSession(enableIndexingAndEvents = true)
|
||||||
@Configuration
|
@Configuration
|
||||||
static class Config {
|
static class Config {
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ import static org.mockito.Mockito.verify;
|
|||||||
@ExtendWith(SpringExtension.class)
|
@ExtendWith(SpringExtension.class)
|
||||||
@ContextConfiguration
|
@ContextConfiguration
|
||||||
@WebAppConfiguration
|
@WebAppConfiguration
|
||||||
class RedisHttpSessionConfigurationOverrideSessionTaskExecutors {
|
class RedisIndexedHttpSessionConfigurationOverrideSessionTaskExecutors {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
RedisMessageListenerContainer redisMessageListenerContainer;
|
RedisMessageListenerContainer redisMessageListenerContainer;
|
||||||
@@ -67,7 +67,7 @@ class RedisHttpSessionConfigurationOverrideSessionTaskExecutors {
|
|||||||
verify(this.springSessionRedisTaskExecutor, never()).execute(any(Runnable.class));
|
verify(this.springSessionRedisTaskExecutor, never()).execute(any(Runnable.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@EnableRedisHttpSession
|
@EnableRedisHttpSession(enableIndexingAndEvents = true)
|
||||||
@Configuration
|
@Configuration
|
||||||
static class Config {
|
static class Config {
|
||||||
|
|
||||||
@@ -0,0 +1,471 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2014-2022 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.session.data.redis.config.annotation.web.http;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.BeanCreationException;
|
||||||
|
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Primary;
|
||||||
|
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
|
||||||
|
import org.springframework.core.annotation.Order;
|
||||||
|
import org.springframework.data.redis.connection.RedisConnection;
|
||||||
|
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||||
|
import org.springframework.data.redis.connection.SubscriptionListener;
|
||||||
|
import org.springframework.data.redis.core.RedisOperations;
|
||||||
|
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
|
||||||
|
import org.springframework.mock.env.MockEnvironment;
|
||||||
|
import org.springframework.session.FlushMode;
|
||||||
|
import org.springframework.session.IndexResolver;
|
||||||
|
import org.springframework.session.SaveMode;
|
||||||
|
import org.springframework.session.Session;
|
||||||
|
import org.springframework.session.config.SessionRepositoryCustomizer;
|
||||||
|
import org.springframework.session.data.redis.RedisFlushMode;
|
||||||
|
import org.springframework.session.data.redis.RedisIndexedSessionRepository;
|
||||||
|
import org.springframework.session.data.redis.config.annotation.SpringSessionRedisConnectionFactory;
|
||||||
|
import org.springframework.test.util.ReflectionTestUtils;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.BDDMockito.given;
|
||||||
|
import static org.mockito.BDDMockito.willAnswer;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link RedisIndexedHttpSessionConfiguration}.
|
||||||
|
*/
|
||||||
|
class RedisIndexedHttpSessionConfigurationTests {
|
||||||
|
|
||||||
|
private static final int MAX_INACTIVE_INTERVAL_IN_SECONDS = 600;
|
||||||
|
|
||||||
|
private static final String CLEANUP_CRON_EXPRESSION = "0 0 * * * *";
|
||||||
|
|
||||||
|
private AnnotationConfigApplicationContext context;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void before() {
|
||||||
|
this.context = new AnnotationConfigApplicationContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
void after() {
|
||||||
|
if (this.context != null) {
|
||||||
|
this.context.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void resolveValue() {
|
||||||
|
registerAndRefresh(RedisConfig.class, CustomRedisHttpSessionConfiguration.class);
|
||||||
|
RedisIndexedHttpSessionConfiguration configuration = this.context
|
||||||
|
.getBean(RedisIndexedHttpSessionConfiguration.class);
|
||||||
|
assertThat(ReflectionTestUtils.getField(configuration, "redisNamespace")).isEqualTo("myRedisNamespace");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void resolveValueByPlaceholder() {
|
||||||
|
this.context
|
||||||
|
.setEnvironment(new MockEnvironment().withProperty("session.redis.namespace", "customRedisNamespace"));
|
||||||
|
registerAndRefresh(RedisConfig.class, PropertySourceConfiguration.class,
|
||||||
|
CustomRedisHttpSessionConfiguration2.class);
|
||||||
|
RedisIndexedHttpSessionConfiguration configuration = this.context
|
||||||
|
.getBean(RedisIndexedHttpSessionConfiguration.class);
|
||||||
|
assertThat(ReflectionTestUtils.getField(configuration, "redisNamespace")).isEqualTo("customRedisNamespace");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void customFlushImmediately() {
|
||||||
|
registerAndRefresh(RedisConfig.class, CustomFlushImmediatelyConfiguration.class);
|
||||||
|
RedisIndexedSessionRepository sessionRepository = this.context.getBean(RedisIndexedSessionRepository.class);
|
||||||
|
assertThat(sessionRepository).isNotNull();
|
||||||
|
assertThat(ReflectionTestUtils.getField(sessionRepository, "flushMode")).isEqualTo(FlushMode.IMMEDIATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void customFlushImmediatelyLegacy() {
|
||||||
|
registerAndRefresh(RedisConfig.class, CustomFlushImmediatelyLegacyConfiguration.class);
|
||||||
|
RedisIndexedSessionRepository sessionRepository = this.context.getBean(RedisIndexedSessionRepository.class);
|
||||||
|
assertThat(sessionRepository).isNotNull();
|
||||||
|
assertThat(ReflectionTestUtils.getField(sessionRepository, "flushMode")).isEqualTo(FlushMode.IMMEDIATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void setCustomFlushImmediately() {
|
||||||
|
registerAndRefresh(RedisConfig.class, CustomFlushImmediatelySetConfiguration.class);
|
||||||
|
RedisIndexedSessionRepository sessionRepository = this.context.getBean(RedisIndexedSessionRepository.class);
|
||||||
|
assertThat(sessionRepository).isNotNull();
|
||||||
|
assertThat(ReflectionTestUtils.getField(sessionRepository, "flushMode")).isEqualTo(FlushMode.IMMEDIATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void setCustomFlushImmediatelyLegacy() {
|
||||||
|
registerAndRefresh(RedisConfig.class, CustomFlushImmediatelySetLegacyConfiguration.class);
|
||||||
|
RedisIndexedSessionRepository sessionRepository = this.context.getBean(RedisIndexedSessionRepository.class);
|
||||||
|
assertThat(sessionRepository).isNotNull();
|
||||||
|
assertThat(ReflectionTestUtils.getField(sessionRepository, "flushMode")).isEqualTo(FlushMode.IMMEDIATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void customCleanupCronSetter() {
|
||||||
|
registerAndRefresh(RedisConfig.class, CustomCleanupCronExpressionSetterConfiguration.class);
|
||||||
|
|
||||||
|
RedisIndexedHttpSessionConfiguration configuration = this.context
|
||||||
|
.getBean(RedisIndexedHttpSessionConfiguration.class);
|
||||||
|
assertThat(configuration).isNotNull();
|
||||||
|
assertThat(ReflectionTestUtils.getField(configuration, "cleanupCron")).isEqualTo(CLEANUP_CRON_EXPRESSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void customSaveModeAnnotation() {
|
||||||
|
registerAndRefresh(RedisConfig.class, CustomSaveModeExpressionAnnotationConfiguration.class);
|
||||||
|
assertThat(this.context.getBean(RedisIndexedSessionRepository.class)).hasFieldOrPropertyWithValue("saveMode",
|
||||||
|
SaveMode.ALWAYS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void customSaveModeSetter() {
|
||||||
|
registerAndRefresh(RedisConfig.class, CustomSaveModeExpressionSetterConfiguration.class);
|
||||||
|
assertThat(this.context.getBean(RedisIndexedSessionRepository.class)).hasFieldOrPropertyWithValue("saveMode",
|
||||||
|
SaveMode.ALWAYS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void qualifiedConnectionFactoryRedisConfig() {
|
||||||
|
registerAndRefresh(RedisConfig.class, QualifiedConnectionFactoryRedisConfig.class);
|
||||||
|
|
||||||
|
RedisIndexedSessionRepository repository = this.context.getBean(RedisIndexedSessionRepository.class);
|
||||||
|
RedisConnectionFactory redisConnectionFactory = this.context.getBean("qualifiedRedisConnectionFactory",
|
||||||
|
RedisConnectionFactory.class);
|
||||||
|
assertThat(repository).isNotNull();
|
||||||
|
assertThat(redisConnectionFactory).isNotNull();
|
||||||
|
RedisOperations redisOperations = (RedisOperations) ReflectionTestUtils.getField(repository,
|
||||||
|
"sessionRedisOperations");
|
||||||
|
assertThat(redisOperations).isNotNull();
|
||||||
|
assertThat(ReflectionTestUtils.getField(redisOperations, "connectionFactory"))
|
||||||
|
.isEqualTo(redisConnectionFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void primaryConnectionFactoryRedisConfig() {
|
||||||
|
registerAndRefresh(RedisConfig.class, PrimaryConnectionFactoryRedisConfig.class);
|
||||||
|
|
||||||
|
RedisIndexedSessionRepository repository = this.context.getBean(RedisIndexedSessionRepository.class);
|
||||||
|
RedisConnectionFactory redisConnectionFactory = this.context.getBean("primaryRedisConnectionFactory",
|
||||||
|
RedisConnectionFactory.class);
|
||||||
|
assertThat(repository).isNotNull();
|
||||||
|
assertThat(redisConnectionFactory).isNotNull();
|
||||||
|
RedisOperations redisOperations = (RedisOperations) ReflectionTestUtils.getField(repository,
|
||||||
|
"sessionRedisOperations");
|
||||||
|
assertThat(redisOperations).isNotNull();
|
||||||
|
assertThat(ReflectionTestUtils.getField(redisOperations, "connectionFactory"))
|
||||||
|
.isEqualTo(redisConnectionFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void qualifiedAndPrimaryConnectionFactoryRedisConfig() {
|
||||||
|
registerAndRefresh(RedisConfig.class, QualifiedAndPrimaryConnectionFactoryRedisConfig.class);
|
||||||
|
|
||||||
|
RedisIndexedSessionRepository repository = this.context.getBean(RedisIndexedSessionRepository.class);
|
||||||
|
RedisConnectionFactory redisConnectionFactory = this.context.getBean("qualifiedRedisConnectionFactory",
|
||||||
|
RedisConnectionFactory.class);
|
||||||
|
assertThat(repository).isNotNull();
|
||||||
|
assertThat(redisConnectionFactory).isNotNull();
|
||||||
|
RedisOperations redisOperations = (RedisOperations) ReflectionTestUtils.getField(repository,
|
||||||
|
"sessionRedisOperations");
|
||||||
|
assertThat(redisOperations).isNotNull();
|
||||||
|
assertThat(ReflectionTestUtils.getField(redisOperations, "connectionFactory"))
|
||||||
|
.isEqualTo(redisConnectionFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void namedConnectionFactoryRedisConfig() {
|
||||||
|
registerAndRefresh(RedisConfig.class, NamedConnectionFactoryRedisConfig.class);
|
||||||
|
|
||||||
|
RedisIndexedSessionRepository repository = this.context.getBean(RedisIndexedSessionRepository.class);
|
||||||
|
RedisConnectionFactory redisConnectionFactory = this.context.getBean("redisConnectionFactory",
|
||||||
|
RedisConnectionFactory.class);
|
||||||
|
assertThat(repository).isNotNull();
|
||||||
|
assertThat(redisConnectionFactory).isNotNull();
|
||||||
|
RedisOperations redisOperations = (RedisOperations) ReflectionTestUtils.getField(repository,
|
||||||
|
"sessionRedisOperations");
|
||||||
|
assertThat(redisOperations).isNotNull();
|
||||||
|
assertThat(ReflectionTestUtils.getField(redisOperations, "connectionFactory"))
|
||||||
|
.isEqualTo(redisConnectionFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void multipleConnectionFactoryRedisConfig() {
|
||||||
|
assertThatExceptionOfType(BeanCreationException.class)
|
||||||
|
.isThrownBy(() -> registerAndRefresh(RedisConfig.class, MultipleConnectionFactoryRedisConfig.class))
|
||||||
|
.withMessageContaining("expected single matching bean but found 2");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void customIndexResolverConfiguration() {
|
||||||
|
registerAndRefresh(RedisConfig.class, CustomIndexResolverConfiguration.class);
|
||||||
|
RedisIndexedSessionRepository repository = this.context.getBean(RedisIndexedSessionRepository.class);
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
IndexResolver<Session> indexResolver = this.context.getBean(IndexResolver.class);
|
||||||
|
assertThat(repository).isNotNull();
|
||||||
|
assertThat(indexResolver).isNotNull();
|
||||||
|
assertThat(repository).hasFieldOrPropertyWithValue("indexResolver", indexResolver);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test // gh-1252
|
||||||
|
void customRedisMessageListenerContainerConfig() {
|
||||||
|
registerAndRefresh(RedisConfig.class, CustomRedisMessageListenerContainerConfig.class);
|
||||||
|
Map<String, RedisMessageListenerContainer> beans = this.context
|
||||||
|
.getBeansOfType(RedisMessageListenerContainer.class);
|
||||||
|
assertThat(beans).hasSize(2);
|
||||||
|
assertThat(beans).containsKeys("springSessionRedisMessageListenerContainer", "redisMessageListenerContainer");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void sessionRepositoryCustomizer() {
|
||||||
|
registerAndRefresh(RedisConfig.class, SessionRepositoryCustomizerConfiguration.class);
|
||||||
|
RedisIndexedSessionRepository sessionRepository = this.context.getBean(RedisIndexedSessionRepository.class);
|
||||||
|
assertThat(sessionRepository).hasFieldOrPropertyWithValue("defaultMaxInactiveInterval",
|
||||||
|
MAX_INACTIVE_INTERVAL_IN_SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerAndRefresh(Class<?>... annotatedClasses) {
|
||||||
|
this.context.register(annotatedClasses);
|
||||||
|
this.context.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static RedisConnectionFactory mockRedisConnectionFactory() {
|
||||||
|
RedisConnectionFactory connectionFactoryMock = mock(RedisConnectionFactory.class);
|
||||||
|
RedisConnection connectionMock = mock(RedisConnection.class);
|
||||||
|
given(connectionFactoryMock.getConnection()).willReturn(connectionMock);
|
||||||
|
|
||||||
|
Properties keyspaceEventsConfig = new Properties();
|
||||||
|
keyspaceEventsConfig.put("notify-keyspace-events", "KEA");
|
||||||
|
given(connectionMock.getConfig("notify-keyspace-events")).willReturn(keyspaceEventsConfig);
|
||||||
|
|
||||||
|
willAnswer((it) -> {
|
||||||
|
SubscriptionListener listener = it.getArgument(0);
|
||||||
|
listener.onPatternSubscribed(it.getArgument(1), 0);
|
||||||
|
listener.onChannelSubscribed("__keyevent@0__:del".getBytes(), 0);
|
||||||
|
listener.onChannelSubscribed("__keyevent@0__:expired".getBytes(), 0);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}).given(connectionMock).pSubscribe(any(), any());
|
||||||
|
|
||||||
|
return connectionFactoryMock;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
static class PropertySourceConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
|
||||||
|
return new PropertySourcesPlaceholderConfigurer();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
static class RedisConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
RedisConnectionFactory defaultRedisConnectionFactory() {
|
||||||
|
return mockRedisConnectionFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
static class CustomFlushImmediatelySetConfiguration extends RedisIndexedHttpSessionConfiguration {
|
||||||
|
|
||||||
|
CustomFlushImmediatelySetConfiguration() {
|
||||||
|
setFlushMode(FlushMode.IMMEDIATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
static class CustomFlushImmediatelySetLegacyConfiguration extends RedisIndexedHttpSessionConfiguration {
|
||||||
|
|
||||||
|
CustomFlushImmediatelySetLegacyConfiguration() {
|
||||||
|
setRedisFlushMode(RedisFlushMode.IMMEDIATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableRedisHttpSession(flushMode = FlushMode.IMMEDIATE, enableIndexingAndEvents = true)
|
||||||
|
static class CustomFlushImmediatelyConfiguration {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableRedisHttpSession(redisFlushMode = RedisFlushMode.IMMEDIATE, enableIndexingAndEvents = true)
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
static class CustomFlushImmediatelyLegacyConfiguration {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
static class CustomCleanupCronExpressionSetterConfiguration extends RedisIndexedHttpSessionConfiguration {
|
||||||
|
|
||||||
|
CustomCleanupCronExpressionSetterConfiguration() {
|
||||||
|
setCleanupCron(CLEANUP_CRON_EXPRESSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableRedisHttpSession(saveMode = SaveMode.ALWAYS, enableIndexingAndEvents = true)
|
||||||
|
static class CustomSaveModeExpressionAnnotationConfiguration {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
static class CustomSaveModeExpressionSetterConfiguration extends RedisIndexedHttpSessionConfiguration {
|
||||||
|
|
||||||
|
CustomSaveModeExpressionSetterConfiguration() {
|
||||||
|
setSaveMode(SaveMode.ALWAYS);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableRedisHttpSession(enableIndexingAndEvents = true)
|
||||||
|
static class QualifiedConnectionFactoryRedisConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@SpringSessionRedisConnectionFactory
|
||||||
|
RedisConnectionFactory qualifiedRedisConnectionFactory() {
|
||||||
|
return mockRedisConnectionFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableRedisHttpSession(enableIndexingAndEvents = true)
|
||||||
|
static class PrimaryConnectionFactoryRedisConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Primary
|
||||||
|
RedisConnectionFactory primaryRedisConnectionFactory() {
|
||||||
|
return mockRedisConnectionFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableRedisHttpSession(enableIndexingAndEvents = true)
|
||||||
|
static class QualifiedAndPrimaryConnectionFactoryRedisConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@SpringSessionRedisConnectionFactory
|
||||||
|
RedisConnectionFactory qualifiedRedisConnectionFactory() {
|
||||||
|
return mockRedisConnectionFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Primary
|
||||||
|
RedisConnectionFactory primaryRedisConnectionFactory() {
|
||||||
|
return mockRedisConnectionFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableRedisHttpSession(enableIndexingAndEvents = true)
|
||||||
|
static class NamedConnectionFactoryRedisConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
RedisConnectionFactory redisConnectionFactory() {
|
||||||
|
return mockRedisConnectionFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableRedisHttpSession(enableIndexingAndEvents = true)
|
||||||
|
static class MultipleConnectionFactoryRedisConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
RedisConnectionFactory secondaryRedisConnectionFactory() {
|
||||||
|
return mockRedisConnectionFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableRedisHttpSession(redisNamespace = "myRedisNamespace", enableIndexingAndEvents = true)
|
||||||
|
static class CustomRedisHttpSessionConfiguration {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableRedisHttpSession(redisNamespace = "${session.redis.namespace}", enableIndexingAndEvents = true)
|
||||||
|
static class CustomRedisHttpSessionConfiguration2 {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableRedisHttpSession(enableIndexingAndEvents = true)
|
||||||
|
static class CustomIndexResolverConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
IndexResolver<Session> indexResolver() {
|
||||||
|
return mock(IndexResolver.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableRedisHttpSession(enableIndexingAndEvents = true)
|
||||||
|
static class CustomRedisMessageListenerContainerConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
RedisMessageListenerContainer redisMessageListenerContainer() {
|
||||||
|
return mock(RedisMessageListenerContainer.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableRedisHttpSession(enableIndexingAndEvents = true)
|
||||||
|
static class SessionRepositoryCustomizerConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Order(0)
|
||||||
|
SessionRepositoryCustomizer<RedisIndexedSessionRepository> sessionRepositoryCustomizerOne() {
|
||||||
|
return (sessionRepository) -> sessionRepository.setDefaultMaxInactiveInterval(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Order(1)
|
||||||
|
SessionRepositoryCustomizer<RedisIndexedSessionRepository> sessionRepositoryCustomizerTwo() {
|
||||||
|
return (sessionRepository) -> sessionRepository
|
||||||
|
.setDefaultMaxInactiveInterval(MAX_INACTIVE_INTERVAL_IN_SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<context:annotation-config/>
|
<context:annotation-config/>
|
||||||
<bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration"/>
|
<bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisIndexedHttpSessionConfiguration"/>
|
||||||
|
|
||||||
<bean class="docs.http.AbstractHttpSessionListenerTests"
|
<bean class="docs.http.AbstractHttpSessionListenerTests"
|
||||||
factory-method="createMockRedisConnection"/>
|
factory-method="createMockRedisConnection"/>
|
||||||
|
|||||||
Reference in New Issue
Block a user