Polish configuration classes

This commit is contained in:
Vedran Pavic
2017-11-03 07:11:00 +01:00
parent f5912da089
commit 17e56dda18
9 changed files with 155 additions and 137 deletions

View File

@@ -25,16 +25,19 @@ import java.lang.annotation.Target;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.session.MapSession;
import org.springframework.session.Session;
import org.springframework.session.SessionRepository; 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.RedisOperationsSessionRepository; import org.springframework.session.data.redis.RedisOperationsSessionRepository;
import org.springframework.session.web.http.SessionRepositoryFilter;
/** /**
* Add this annotation to an {@code @Configuration} class to expose the * Add this annotation to an {@code @Configuration} class to expose the
* SessionRepositoryFilter as a bean named "springSessionRepositoryFilter" and backed by * {@link SessionRepositoryFilter} as a bean named {@code springSessionRepositoryFilter}
* Redis. In order to leverage the annotation, a single {@link RedisConnectionFactory} * and backed by Redis. In order to leverage the annotation, a single
* must be provided. For example: * {@link RedisConnectionFactory} must be provided. For example:
* *
* <pre class="code"> * <pre class="code">
* &#064;Configuration * &#064;Configuration
@@ -42,7 +45,7 @@ import org.springframework.session.data.redis.RedisOperationsSessionRepository;
* public class RedisHttpSessionConfig { * public class RedisHttpSessionConfig {
* *
* &#064;Bean * &#064;Bean
* public LettuceConnectionFactory connectionFactory() { * public LettuceConnectionFactory redisConnectionFactory() {
* return new LettuceConnectionFactory(); * return new LettuceConnectionFactory();
* } * }
* *
@@ -68,37 +71,27 @@ public @interface EnableRedisHttpSession {
* This should be a non-negative integer. * This should be a non-negative integer.
* @return the seconds a session can be inactive before expiring * @return the seconds a session can be inactive before expiring
*/ */
int maxInactiveIntervalInSeconds() default 1800; int maxInactiveIntervalInSeconds() default MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS;
/** /**
* <p>
* Defines a unique namespace for keys. The value is used to isolate sessions by * Defines a unique namespace for keys. The value is used to isolate sessions by
* changing the prefix from default {@code spring:session:} to * changing the prefix from default {@code spring:session:} to
* {@code <redisNamespace>:}. * {@code <redisNamespace>:}.
* </p>
*
* <p> * <p>
* For example, if you had an application named "Application A" that needed to keep * For example, if you had an application named "Application A" that needed to keep
* the sessions isolated from "Application B" you could set two different values for * the sessions isolated from "Application B" you could set two different values for
* the applications and they could function within the same Redis instance. * the applications and they could function within the same Redis instance.
* </p>
*
* @return the unique namespace for keys * @return the unique namespace for keys
*/ */
String redisNamespace() default RedisOperationsSessionRepository.DEFAULT_NAMESPACE; String redisNamespace() default RedisOperationsSessionRepository.DEFAULT_NAMESPACE;
/** /**
* Flush mode for the Redis sessions. The default is {@code ON_SAVE} which only
* updates the backing Redis when {@link SessionRepository#save(Session)} is invoked.
* In a web environment this happens just before the HTTP response is committed.
* <p> * <p>
* Sets the flush mode for the Redis sessions. The default is ON_SAVE which only * Setting the value to {@code IMMEDIATE} will ensure that the any updates to the
* updates the backing Redis when * Session are immediately written to the Redis instance.
* {@link SessionRepository#save(org.springframework.session.Session)} is invoked. In
* a web environment this happens just before the HTTP response is committed.
* </p>
* <p>
* Setting the value to IMMEDIATE will ensure that the any updates to the Session are
* immediately written to the Redis instance.
* </p>
*
* @return the {@link RedisFlushMode} to use * @return the {@link RedisFlushMode} to use
* @since 1.1 * @since 1.1
*/ */
@@ -107,6 +100,7 @@ public @interface EnableRedisHttpSession {
/** /**
* The cron expression for expired session cleanup job. By default runs every minute. * The cron expression for expired session cleanup job. By default runs every minute.
* @return the session cleanup cron expression * @return the session cleanup cron expression
* @since 2.0.0
*/ */
String cleanupCron() default RedisHttpSessionConfiguration.DEFAULT_CLEANUP_CRON; String cleanupCron() default RedisHttpSessionConfiguration.DEFAULT_CLEANUP_CRON;

View File

@@ -44,6 +44,7 @@ import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer; import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar; import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.session.MapSession;
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.RedisOperationsSessionRepository; import org.springframework.session.data.redis.RedisOperationsSessionRepository;
@@ -57,7 +58,7 @@ import org.springframework.util.StringValueResolver;
/** /**
* Exposes the {@link SessionRepositoryFilter} as a bean named * Exposes the {@link SessionRepositoryFilter} as a bean named
* "springSessionRepositoryFilter". In order to use this a single * {@code springSessionRepositoryFilter}. In order to use this a single
* {@link RedisConnectionFactory} must be exposed as a Bean. * {@link RedisConnectionFactory} must be exposed as a Bean.
* *
* @author Rob Winch * @author Rob Winch
@@ -73,9 +74,9 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
static final String DEFAULT_CLEANUP_CRON = "0 * * * * *"; static final String DEFAULT_CLEANUP_CRON = "0 * * * * *";
private Integer maxInactiveIntervalInSeconds = 1800; private Integer maxInactiveIntervalInSeconds = MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS;
private String redisNamespace = ""; private String redisNamespace = RedisOperationsSessionRepository.DEFAULT_NAMESPACE;
private RedisFlushMode redisFlushMode = RedisFlushMode.ON_SAVE; private RedisFlushMode redisFlushMode = RedisFlushMode.ON_SAVE;
@@ -115,8 +116,7 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
} }
@Bean @Bean
public RedisMessageListenerContainer redisMessageListenerContainer( public RedisMessageListenerContainer redisMessageListenerContainer() {
RedisOperationsSessionRepository messageListener) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer(); RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(this.redisConnectionFactory); container.setConnectionFactory(this.redisConnectionFactory);
if (this.redisTaskExecutor != null) { if (this.redisTaskExecutor != null) {
@@ -125,12 +125,12 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
if (this.redisSubscriptionExecutor != null) { if (this.redisSubscriptionExecutor != null) {
container.setSubscriptionExecutor(this.redisSubscriptionExecutor); container.setSubscriptionExecutor(this.redisSubscriptionExecutor);
} }
container.addMessageListener(messageListener, container.addMessageListener(sessionRepository(),
Arrays.asList(new PatternTopic("__keyevent@*:del"), Arrays.asList(new PatternTopic("__keyevent@*:del"),
new PatternTopic("__keyevent@*:expired"))); new PatternTopic("__keyevent@*:expired")));
container.addMessageListener(messageListener, container.addMessageListener(sessionRepository(),
Collections.singletonList(new PatternTopic( Collections.singletonList(new PatternTopic(
messageListener.getSessionCreatedChannelPrefix() + "*"))); sessionRepository().getSessionCreatedChannelPrefix() + "*")));
return container; return container;
} }

View File

@@ -17,24 +17,29 @@
package org.springframework.session.data.redis.config.annotation.web.server; package org.springframework.session.data.redis.config.annotation.web.server;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory; import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory;
import org.springframework.session.MapSession;
import org.springframework.session.ReactiveSessionRepository; import org.springframework.session.ReactiveSessionRepository;
import org.springframework.session.Session; import org.springframework.session.Session;
import org.springframework.session.config.annotation.web.server.EnableSpringWebSession; import org.springframework.session.config.annotation.web.server.EnableSpringWebSession;
import org.springframework.session.data.redis.ReactiveRedisOperationsSessionRepository; import org.springframework.session.data.redis.ReactiveRedisOperationsSessionRepository;
import org.springframework.session.data.redis.RedisFlushMode; import org.springframework.session.data.redis.RedisFlushMode;
import org.springframework.web.server.session.WebSessionManager;
/** /**
* Add this annotation to an {@code @Configuration} class to expose the * Add this annotation to an {@code @Configuration} class to expose the
* {@link org.springframework.web.server.session.WebSessionManager} as a bean named * {@link WebSessionManager} as a bean named {@code webSessionManager} and backed by
* {@code webSessionManager} and backed by Reactive Redis. In order to leverage the * Reactive Redis. In order to leverage the annotation, a single
* annotation, a single {@link ReactiveRedisConnectionFactory} must be provided. For * {@link ReactiveRedisConnectionFactory} must be provided. For example:
* example: <pre class="code"> *
* <pre class="code">
* &#064;Configuration * &#064;Configuration
* &#064;EnableRedisWebSession * &#064;EnableRedisWebSession
* public class RedisWebSessionConfig { * public class RedisWebSessionConfig {
@@ -47,52 +52,46 @@ import org.springframework.session.data.redis.RedisFlushMode;
* } * }
* </pre> * </pre>
* *
* More advanced configurations can extend {@link RedisWebSessionConfiguration} * More advanced configurations can extend {@link RedisWebSessionConfiguration} instead.
* instead.
* *
* @author Vedran Pavic * @author Vedran Pavic
* @since 2.0.0 * @since 2.0.0
* @see EnableSpringWebSession * @see EnableSpringWebSession
*/ */
@Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target({ java.lang.annotation.ElementType.TYPE }) @Target(ElementType.TYPE)
@Documented @Documented
@Import(RedisWebSessionConfiguration.class) @Import(RedisWebSessionConfiguration.class)
@Configuration @Configuration
public @interface EnableRedisWebSession { public @interface EnableRedisWebSession {
int maxInactiveIntervalInSeconds() default 1800; /**
* The session timeout in seconds. By default, it is set to 1800 seconds (30 minutes).
* This should be a non-negative integer.
* @return the seconds a session can be inactive before expiring
*/
int maxInactiveIntervalInSeconds() default MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS;
/** /**
* <p>
* Defines a unique namespace for keys. The value is used to isolate sessions by * Defines a unique namespace for keys. The value is used to isolate sessions by
* changing the prefix from default {@code spring:session:} to * changing the prefix from default {@code spring:session:} to
* {@code <redisNamespace>:}. * {@code <redisNamespace>:}.
* </p>
*
* <p> * <p>
* For example, if you had an application named "Application A" that needed to keep * For example, if you had an application named "Application A" that needed to keep
* the sessions isolated from "Application B" you could set two different values for * the sessions isolated from "Application B" you could set two different values for
* the applications and they could function within the same Redis instance. * the applications and they could function within the same Redis instance.
* </p>
*
* @return the unique namespace for keys * @return the unique namespace for keys
*/ */
String redisNamespace() default ReactiveRedisOperationsSessionRepository.DEFAULT_NAMESPACE; String redisNamespace() default ReactiveRedisOperationsSessionRepository.DEFAULT_NAMESPACE;
/** /**
* <p> * Flush mode for the Redis sessions. The default is {@code ON_SAVE} which only
* Sets the flush mode for the Redis sessions. The default is ON_SAVE which only
* updates the backing Redis when {@link ReactiveSessionRepository#save(Session)} is * updates the backing Redis when {@link ReactiveSessionRepository#save(Session)} is
* invoked. In a web environment this happens just before the HTTP response is * invoked. In a web environment this happens just before the HTTP response is
* committed. * committed.
* </p>
*
* <p> * <p>
* Setting the value to IMMEDIATE will ensure that the any updates to the Session are * Setting the value to {@code IMMEDIATE} will ensure that the any updates to the
* immediately written to the Redis instance. * Session are immediately written to the Redis instance.
* </p>
*
* @return the {@link RedisFlushMode} to use * @return the {@link RedisFlushMode} to use
*/ */
RedisFlushMode redisFlushMode() default RedisFlushMode.ON_SAVE; RedisFlushMode redisFlushMode() default RedisFlushMode.ON_SAVE;

View File

@@ -32,6 +32,7 @@ import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer
import org.springframework.data.redis.serializer.RedisSerializationContext; import org.springframework.data.redis.serializer.RedisSerializationContext;
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.session.MapSession;
import org.springframework.session.config.annotation.web.server.SpringWebSessionConfiguration; import org.springframework.session.config.annotation.web.server.SpringWebSessionConfiguration;
import org.springframework.session.data.redis.ReactiveRedisOperationsSessionRepository; import org.springframework.session.data.redis.ReactiveRedisOperationsSessionRepository;
import org.springframework.session.data.redis.RedisFlushMode; import org.springframework.session.data.redis.RedisFlushMode;
@@ -58,9 +59,9 @@ public class RedisWebSessionConfiguration extends SpringWebSessionConfiguration
private static final RedisSerializer<Object> valueSerializer = new JdkSerializationRedisSerializer(); private static final RedisSerializer<Object> valueSerializer = new JdkSerializationRedisSerializer();
private Integer maxInactiveIntervalInSeconds = 1800; private Integer maxInactiveIntervalInSeconds = MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS;
private String redisNamespace = ""; private String redisNamespace = ReactiveRedisOperationsSessionRepository.DEFAULT_NAMESPACE;
private RedisFlushMode redisFlushMode = RedisFlushMode.ON_SAVE; private RedisFlushMode redisFlushMode = RedisFlushMode.ON_SAVE;
@@ -74,13 +75,10 @@ public class RedisWebSessionConfiguration extends SpringWebSessionConfiguration
createDefaultTemplate(this.redisConnectionFactory)); createDefaultTemplate(this.redisConnectionFactory));
sessionRepository sessionRepository
.setDefaultMaxInactiveInterval(this.maxInactiveIntervalInSeconds); .setDefaultMaxInactiveInterval(this.maxInactiveIntervalInSeconds);
if (StringUtils.hasText(this.redisNamespace)) { if (StringUtils.hasText(this.redisNamespace)) {
sessionRepository.setRedisKeyNamespace(this.redisNamespace); sessionRepository.setRedisKeyNamespace(this.redisNamespace);
} }
sessionRepository.setRedisFlushMode(this.redisFlushMode); sessionRepository.setRedisFlushMode(this.redisFlushMode);
return sessionRepository; return sessionRepository;
} }
@@ -116,20 +114,17 @@ public class RedisWebSessionConfiguration extends SpringWebSessionConfiguration
@Override @Override
public void setImportMetadata(AnnotationMetadata importMetadata) { public void setImportMetadata(AnnotationMetadata importMetadata) {
Map<String, Object> enableAttrMap = importMetadata Map<String, Object> attributeMap = importMetadata
.getAnnotationAttributes(EnableRedisWebSession.class.getName()); .getAnnotationAttributes(EnableRedisWebSession.class.getName());
AnnotationAttributes enableAttrs = AnnotationAttributes.fromMap(enableAttrMap); AnnotationAttributes attributes = AnnotationAttributes.fromMap(attributeMap);
this.maxInactiveIntervalInSeconds = attributes
if (enableAttrs != null) { .getNumber("maxInactiveIntervalInSeconds");
this.maxInactiveIntervalInSeconds = enableAttrs String redisNamespaceValue = attributes.getString("redisNamespace");
.getNumber("maxInactiveIntervalInSeconds"); if (StringUtils.hasText(redisNamespaceValue)) {
String redisNamespaceValue = enableAttrs.getString("redisNamespace"); this.redisNamespace = this.embeddedValueResolver
if (StringUtils.hasText(redisNamespaceValue)) { .resolveStringValue(redisNamespaceValue);
this.redisNamespace = this.embeddedValueResolver
.resolveStringValue(redisNamespaceValue);
}
this.redisFlushMode = enableAttrs.getEnum("redisFlushMode");
} }
this.redisFlushMode = attributes.getEnum("redisFlushMode");
} }
private static ReactiveRedisTemplate<String, Object> createDefaultTemplate( private static ReactiveRedisTemplate<String, Object> createDefaultTemplate(
@@ -137,7 +132,6 @@ public class RedisWebSessionConfiguration extends SpringWebSessionConfiguration
RedisSerializationContext<String, Object> serializationContext = RedisSerializationContext RedisSerializationContext<String, Object> serializationContext = RedisSerializationContext
.<String, Object>newSerializationContext(valueSerializer) .<String, Object>newSerializationContext(valueSerializer)
.key(keySerializer).hashKey(keySerializer).build(); .key(keySerializer).hashKey(keySerializer).build();
return new ReactiveRedisTemplate<>(connectionFactory, serializationContext); return new ReactiveRedisTemplate<>(connectionFactory, serializationContext);
} }

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright 2014-2016 the original author or authors. * Copyright 2014-2017 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.
@@ -17,34 +17,41 @@
package org.springframework.session.hazelcast.config.annotation.web.http; package org.springframework.session.hazelcast.config.annotation.web.http;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
import com.hazelcast.core.HazelcastInstance;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.session.MapSession; import org.springframework.session.MapSession;
import org.springframework.session.Session;
import org.springframework.session.SessionRepository; 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.hazelcast.HazelcastFlushMode; import org.springframework.session.hazelcast.HazelcastFlushMode;
import org.springframework.session.web.http.SessionRepositoryFilter;
/** /**
* Add this annotation to a {@code @Configuration} class to expose the * Add this annotation to an {@code @Configuration} class to expose the
* SessionRepositoryFilter as a bean named "springSessionRepositoryFilter" and backed by * {@link SessionRepositoryFilter} as a bean named {@code springSessionRepositoryFilter}
* Hazelcast. In order to leverage the annotation, a single HazelcastInstance must be * and backed by Hazelcast. In order to leverage the annotation, a single
* provided. For example: <pre> * {@link HazelcastInstance} must be provided. For example:
* <code> *
* {@literal @Configuration} * <pre class="code">
* {@literal @EnableHazelcastHttpSession} * &#064;Configuration
* &#064;EnableHazelcastHttpSession
* public class HazelcastHttpSessionConfig { * public class HazelcastHttpSessionConfig {
* *
* {@literal @Bean} * &#064;Bean
* public HazelcastInstance embeddedHazelcast() { * public HazelcastInstance embeddedHazelcast() {
* Config hazelcastConfig = new Config(); * Config hazelcastConfig = new Config();
* return Hazelcast.newHazelcastInstance(hazelcastConfig); * return Hazelcast.newHazelcastInstance(hazelcastConfig);
* } * }
* *
* } * }
* </code> </pre> * </pre>
* *
* More advanced configurations can extend {@link HazelcastHttpSessionConfiguration} * More advanced configurations can extend {@link HazelcastHttpSessionConfiguration}
* instead. * instead.
@@ -54,33 +61,33 @@ import org.springframework.session.hazelcast.HazelcastFlushMode;
* @since 1.1 * @since 1.1
* @see EnableSpringHttpSession * @see EnableSpringHttpSession
*/ */
@Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target({ java.lang.annotation.ElementType.TYPE }) @Target(ElementType.TYPE)
@Documented @Documented
@Import(HazelcastHttpSessionConfiguration.class) @Import(HazelcastHttpSessionConfiguration.class)
@Configuration @Configuration
public @interface EnableHazelcastHttpSession { public @interface EnableHazelcastHttpSession {
/** /**
* This is the session timeout in seconds. By default, it is set to 1800 seconds (30 * The session timeout in seconds. By default, it is set to 1800 seconds (30 minutes).
* minutes). This should be a non-negative integer. * This should be a non-negative integer.
*
* @return the seconds a session can be inactive before expiring * @return the seconds a session can be inactive before expiring
*/ */
int maxInactiveIntervalInSeconds() default MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS; int maxInactiveIntervalInSeconds() default MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS;
/** /**
* This is the name of the Map that will be used in Hazelcast to store the session * This is the name of the Map that will be used in Hazelcast to store the session
* data. Default is {@link HazelcastHttpSessionConfiguration#DEFAULT_SESSION_MAP_NAME}. * data. Default is
* {@link HazelcastHttpSessionConfiguration#DEFAULT_SESSION_MAP_NAME}.
* @return the name of the Map to store the sessions in Hazelcast * @return the name of the Map to store the sessions in Hazelcast
*/ */
String sessionMapName() default HazelcastHttpSessionConfiguration.DEFAULT_SESSION_MAP_NAME; String sessionMapName() default HazelcastHttpSessionConfiguration.DEFAULT_SESSION_MAP_NAME;
/** /**
* Flush mode for the Hazelcast sessions. The default is {@code ON_SAVE} which only * Flush mode for the Hazelcast sessions. The default is {@code ON_SAVE} which only
* updates the backing Hazelcast when * updates the backing Hazelcast when {@link SessionRepository#save(Session)} is
* {@link SessionRepository#save(org.springframework.session.Session)} is invoked. In * invoked. In a web environment this happens just before the HTTP response is
* a web environment this happens just before the HTTP response is committed. * committed.
* <p> * <p>
* Setting the value to {@code IMMEDIATE} will ensure that the any updates to the * Setting the value to {@code IMMEDIATE} will ensure that the any updates to the
* Session are immediately written to the Hazelcast instance. * Session are immediately written to the Hazelcast instance.

View File

@@ -22,6 +22,7 @@ import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.core.IMap; import com.hazelcast.core.IMap;
import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@@ -34,10 +35,11 @@ import org.springframework.session.hazelcast.HazelcastFlushMode;
import org.springframework.session.hazelcast.HazelcastSessionRepository; import org.springframework.session.hazelcast.HazelcastSessionRepository;
import org.springframework.session.hazelcast.config.annotation.SpringSessionHazelcastInstance; import org.springframework.session.hazelcast.config.annotation.SpringSessionHazelcastInstance;
import org.springframework.session.web.http.SessionRepositoryFilter; import org.springframework.session.web.http.SessionRepositoryFilter;
import org.springframework.util.StringUtils;
/** /**
* Exposes the {@link SessionRepositoryFilter} as a bean named * Exposes the {@link SessionRepositoryFilter} as a bean named
* "springSessionRepositoryFilter". In order to use this a single * {@code springSessionRepositoryFilter}. In order to use this a single
* {@link HazelcastInstance} must be exposed as a Bean. * {@link HazelcastInstance} must be exposed as a Bean.
* *
* @author Tommy Ludwig * @author Tommy Ludwig
@@ -57,38 +59,23 @@ public class HazelcastHttpSessionConfiguration extends SpringHttpSessionConfigur
private HazelcastFlushMode hazelcastFlushMode = HazelcastFlushMode.ON_SAVE; private HazelcastFlushMode hazelcastFlushMode = HazelcastFlushMode.ON_SAVE;
private HazelcastInstance hazelcastInstance;
private ApplicationEventPublisher applicationEventPublisher;
@Bean @Bean
public HazelcastSessionRepository sessionRepository( public HazelcastSessionRepository sessionRepository() {
@SpringSessionHazelcastInstance ObjectProvider<HazelcastInstance> springSessionHazelcastInstance, IMap<String, MapSession> sessions = this.hazelcastInstance
ObjectProvider<HazelcastInstance> hazelcastInstance,
ApplicationEventPublisher eventPublisher) {
HazelcastInstance hazelcastInstanceToUse = springSessionHazelcastInstance
.getIfAvailable();
if (hazelcastInstanceToUse == null) {
hazelcastInstanceToUse = hazelcastInstance.getObject();
}
IMap<String, MapSession> sessions = hazelcastInstanceToUse
.getMap(this.sessionMapName); .getMap(this.sessionMapName);
HazelcastSessionRepository sessionRepository = new HazelcastSessionRepository( HazelcastSessionRepository sessionRepository = new HazelcastSessionRepository(
sessions); sessions);
sessionRepository.setApplicationEventPublisher(eventPublisher); sessionRepository.setApplicationEventPublisher(this.applicationEventPublisher);
sessionRepository sessionRepository
.setDefaultMaxInactiveInterval(this.maxInactiveIntervalInSeconds); .setDefaultMaxInactiveInterval(this.maxInactiveIntervalInSeconds);
sessionRepository.setHazelcastFlushMode(this.hazelcastFlushMode); sessionRepository.setHazelcastFlushMode(this.hazelcastFlushMode);
return sessionRepository; return sessionRepository;
} }
@Override
public void setImportMetadata(AnnotationMetadata importMetadata) {
Map<String, Object> enableAttrMap = importMetadata
.getAnnotationAttributes(EnableHazelcastHttpSession.class.getName());
AnnotationAttributes enableAttrs = AnnotationAttributes.fromMap(enableAttrMap);
setMaxInactiveIntervalInSeconds(
enableAttrs.getNumber("maxInactiveIntervalInSeconds"));
setSessionMapName(enableAttrs.getString("sessionMapName"));
setHazelcastFlushMode(enableAttrs.getEnum("hazelcastFlushMode"));
}
public void setMaxInactiveIntervalInSeconds(int maxInactiveIntervalInSeconds) { public void setMaxInactiveIntervalInSeconds(int maxInactiveIntervalInSeconds) {
this.maxInactiveIntervalInSeconds = maxInactiveIntervalInSeconds; this.maxInactiveIntervalInSeconds = maxInactiveIntervalInSeconds;
} }
@@ -101,4 +88,36 @@ public class HazelcastHttpSessionConfiguration extends SpringHttpSessionConfigur
this.hazelcastFlushMode = hazelcastFlushMode; this.hazelcastFlushMode = hazelcastFlushMode;
} }
@Autowired
public void setHazelcastInstance(
@SpringSessionHazelcastInstance ObjectProvider<HazelcastInstance> springSessionHazelcastInstance,
ObjectProvider<HazelcastInstance> hazelcastInstance) {
HazelcastInstance hazelcastInstanceToUse = springSessionHazelcastInstance
.getIfAvailable();
if (hazelcastInstanceToUse == null) {
hazelcastInstanceToUse = hazelcastInstance.getObject();
}
this.hazelcastInstance = hazelcastInstanceToUse;
}
@Autowired
public void setApplicationEventPublisher(
ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
@Override
public void setImportMetadata(AnnotationMetadata importMetadata) {
Map<String, Object> attributeMap = importMetadata
.getAnnotationAttributes(EnableHazelcastHttpSession.class.getName());
AnnotationAttributes attributes = AnnotationAttributes.fromMap(attributeMap);
this.maxInactiveIntervalInSeconds =
attributes.getNumber("maxInactiveIntervalInSeconds");
String sessionMapNameValue = attributes.getString("sessionMapName");
if (StringUtils.hasText(sessionMapNameValue)) {
this.sessionMapName = sessionMapNameValue;
}
this.hazelcastFlushMode = attributes.getEnum("hazelcastFlushMode");
}
} }

View File

@@ -204,14 +204,14 @@ public class HazelcastHttpSessionConfigurationTests {
HazelcastInstance.class); HazelcastInstance.class);
assertThat(repository).isNotNull(); assertThat(repository).isNotNull();
assertThat(hazelcastInstance).isNotNull(); assertThat(hazelcastInstance).isNotNull();
assertThat(ReflectionTestUtils.getField(repository, "sessions")).isEqualTo( assertThat(ReflectionTestUtils.getField(repository, "sessions"))
NamedHazelcastInstanceConfiguration.hazelcastInstanceSessions); .isEqualTo(NamedHazelcastInstanceConfiguration.hazelcastInstanceSessions);
} }
@Test @Test
public void multipleDataSourceConfiguration() { public void multipleDataSourceConfiguration() {
this.thrown.expect(BeanCreationException.class); this.thrown.expect(BeanCreationException.class);
this.thrown.expectMessage("sessionRepository"); this.thrown.expectMessage("expected single matching bean but found 2");
registerAndRefresh(MultipleHazelcastInstanceConfiguration.class); registerAndRefresh(MultipleHazelcastInstanceConfiguration.class);
} }

View File

@@ -22,17 +22,20 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
import javax.sql.DataSource;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.session.MapSession; import org.springframework.session.MapSession;
import org.springframework.session.config.annotation.web.http.EnableSpringHttpSession; import org.springframework.session.config.annotation.web.http.EnableSpringHttpSession;
import org.springframework.session.jdbc.JdbcOperationsSessionRepository; import org.springframework.session.jdbc.JdbcOperationsSessionRepository;
import org.springframework.session.web.http.SessionRepositoryFilter;
/** /**
* Add this annotation to an {@code @Configuration} class to expose the * Add this annotation to an {@code @Configuration} class to expose the
* SessionRepositoryFilter as a bean named "springSessionRepositoryFilter" and backed by a * {@link SessionRepositoryFilter} as a bean named {@code springSessionRepositoryFilter}
* relational database. In order to leverage the annotation, a single * and backed by a relational database. In order to leverage the annotation, a single
* {@link javax.sql.DataSource} must be provided. For example: * {@link DataSource} must be provided. For example:
* *
* <pre class="code"> * <pre class="code">
* &#064;Configuration * &#064;Configuration
@@ -73,22 +76,23 @@ import org.springframework.session.jdbc.JdbcOperationsSessionRepository;
@Configuration @Configuration
public @interface EnableJdbcHttpSession { public @interface EnableJdbcHttpSession {
/**
* The session timeout in seconds. By default, it is set to 1800 seconds (30 minutes).
* This should be a non-negative integer.
* @return the seconds a session can be inactive before expiring
*/
int maxInactiveIntervalInSeconds() default MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS;
/** /**
* The name of database table used by Spring Session to store sessions. * The name of database table used by Spring Session to store sessions.
* @return the database table name * @return the database table name
*/ */
String tableName() default JdbcOperationsSessionRepository.DEFAULT_TABLE_NAME; String tableName() default JdbcOperationsSessionRepository.DEFAULT_TABLE_NAME;
/**
* The session timeout in seconds. By default, it is set to 1800 seconds (30
* minutes). This should be a non-negative integer.
* @return the seconds a session can be inactive before expiring
*/
int maxInactiveIntervalInSeconds() default MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS;
/** /**
* The cron expression for expired session cleanup job. By default runs every minute. * The cron expression for expired session cleanup job. By default runs every minute.
* @return the session cleanup cron expression * @return the session cleanup cron expression
* @since 2.0.0
*/ */
String cleanupCron() default JdbcHttpSessionConfiguration.DEFAULT_CLEANUP_CRON; String cleanupCron() default JdbcHttpSessionConfiguration.DEFAULT_CLEANUP_CRON;

View File

@@ -42,17 +42,18 @@ import org.springframework.session.MapSession;
import org.springframework.session.config.annotation.web.http.SpringHttpSessionConfiguration; import org.springframework.session.config.annotation.web.http.SpringHttpSessionConfiguration;
import org.springframework.session.jdbc.JdbcOperationsSessionRepository; import org.springframework.session.jdbc.JdbcOperationsSessionRepository;
import org.springframework.session.jdbc.config.annotation.SpringSessionDataSource; import org.springframework.session.jdbc.config.annotation.SpringSessionDataSource;
import org.springframework.session.web.http.SessionRepositoryFilter;
import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.util.StringValueResolver; import org.springframework.util.StringValueResolver;
/** /**
* Spring @Configuration class used to configure and initialize a JDBC based HttpSession * Spring {@code @Configuration} class used to configure and initialize a JDBC based
* provider implementation in Spring Session. * {@code HttpSession} provider implementation in Spring Session.
* <p> * <p>
* Exposes the {@link org.springframework.session.web.http.SessionRepositoryFilter} as a * Exposes the {@link SessionRepositoryFilter} as a bean named
* bean named "springSessionRepositoryFilter". In order to use this a single * {@code springSessionRepositoryFilter}. In order to use this a single {@link DataSource}
* {@link DataSource} must be exposed as a Bean. * must be exposed as a Bean.
* *
* @author Vedran Pavic * @author Vedran Pavic
* @author Eddú Meléndez * @author Eddú Meléndez
@@ -67,10 +68,10 @@ public class JdbcHttpSessionConfiguration extends SpringHttpSessionConfiguration
static final String DEFAULT_CLEANUP_CRON = "0 * * * * *"; static final String DEFAULT_CLEANUP_CRON = "0 * * * * *";
private String tableName = JdbcOperationsSessionRepository.DEFAULT_TABLE_NAME;
private Integer maxInactiveIntervalInSeconds = MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS; private Integer maxInactiveIntervalInSeconds = MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS;
private String tableName = JdbcOperationsSessionRepository.DEFAULT_TABLE_NAME;
private String cleanupCron = DEFAULT_CLEANUP_CRON; private String cleanupCron = DEFAULT_CLEANUP_CRON;
private DataSource dataSource; private DataSource dataSource;
@@ -112,14 +113,14 @@ public class JdbcHttpSessionConfiguration extends SpringHttpSessionConfiguration
return sessionRepository; return sessionRepository;
} }
public void setTableName(String tableName) {
this.tableName = tableName;
}
public void setMaxInactiveIntervalInSeconds(Integer maxInactiveIntervalInSeconds) { public void setMaxInactiveIntervalInSeconds(Integer maxInactiveIntervalInSeconds) {
this.maxInactiveIntervalInSeconds = maxInactiveIntervalInSeconds; this.maxInactiveIntervalInSeconds = maxInactiveIntervalInSeconds;
} }
public void setTableName(String tableName) {
this.tableName = tableName;
}
public void setCleanupCron(String cleanupCron) { public void setCleanupCron(String cleanupCron) {
this.cleanupCron = cleanupCron; this.cleanupCron = cleanupCron;
} }
@@ -173,16 +174,16 @@ public class JdbcHttpSessionConfiguration extends SpringHttpSessionConfiguration
Map<String, Object> attributeMap = importMetadata Map<String, Object> attributeMap = importMetadata
.getAnnotationAttributes(EnableJdbcHttpSession.class.getName()); .getAnnotationAttributes(EnableJdbcHttpSession.class.getName());
AnnotationAttributes attributes = AnnotationAttributes.fromMap(attributeMap); AnnotationAttributes attributes = AnnotationAttributes.fromMap(attributeMap);
this.maxInactiveIntervalInSeconds = attributes
.getNumber("maxInactiveIntervalInSeconds");
String tableNameValue = attributes.getString("tableName"); String tableNameValue = attributes.getString("tableName");
if (StringUtils.hasText(tableNameValue)) { if (StringUtils.hasText(tableNameValue)) {
this.tableName = this.embeddedValueResolver this.tableName = this.embeddedValueResolver
.resolveStringValue(tableNameValue); .resolveStringValue(tableNameValue);
} }
this.maxInactiveIntervalInSeconds = attributes
.getNumber("maxInactiveIntervalInSeconds");
String cleanupCron = attributes.getString("cleanupCron"); String cleanupCron = attributes.getString("cleanupCron");
if (StringUtils.hasText(cleanupCron)) { if (StringUtils.hasText(cleanupCron)) {
this.cleanupCron = this.embeddedValueResolver.resolveStringValue(cleanupCron); this.cleanupCron = cleanupCron;
} }
} }