Add support for configuring JDBC session cleanup cron

This commit is contained in:
Vedran Pavic
2017-10-26 13:04:12 +02:00
parent fe3f40c6f4
commit 97698fd590
6 changed files with 222 additions and 258 deletions

View File

@@ -48,7 +48,6 @@ import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.support.lob.DefaultLobHandler;
import org.springframework.jdbc.support.lob.LobHandler;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.session.FindByIndexNameSessionRepository;
import org.springframework.session.MapSession;
import org.springframework.session.Session;
@@ -535,7 +534,6 @@ public class JdbcOperationsSessionRepository implements
return sessionMap;
}
@Scheduled(cron = "${spring.session.cleanup.cron.expression:0 * * * * *}")
public void cleanUpExpiredSessions() {
Integer deletedCount = this.transactionOperations.execute(transactionStatus ->
JdbcOperationsSessionRepository.this.jdbcOperations.update(

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");
* you may not use this file except in compliance with the License.
@@ -58,8 +58,8 @@ import org.springframework.session.jdbc.JdbcOperationsSessionRepository;
* More advanced configurations can extend {@link JdbcHttpSessionConfiguration} instead.
*
* For additional information on how to configure data access related concerns, please
* refer to the
* <a href="http://docs.spring.io/spring/docs/current/spring-framework-reference/html/spring-data-tier.html">
* refer to the <a href=
* "http://docs.spring.io/spring/docs/current/spring-framework-reference/html/spring-data-tier.html">
* Spring Framework Reference Documentation</a>.
*
* @author Vedran Pavic
@@ -80,11 +80,16 @@ public @interface EnableJdbcHttpSession {
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.
*
* 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.
* @return the session cleanup cron expression
*/
String cleanupCron() default JdbcHttpSessionConfiguration.DEFAULT_CLEANUP_CRON;
}

View File

@@ -28,7 +28,6 @@ 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.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.GenericConversionService;
@@ -37,6 +36,9 @@ import org.springframework.core.serializer.support.SerializingConverter;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.jdbc.support.lob.LobHandler;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.session.MapSession;
import org.springframework.session.config.annotation.web.http.SpringHttpSessionConfiguration;
import org.springframework.session.jdbc.JdbcOperationsSessionRepository;
import org.springframework.transaction.PlatformTransactionManager;
@@ -59,38 +61,37 @@ import org.springframework.util.StringValueResolver;
@Configuration
@EnableScheduling
public class JdbcHttpSessionConfiguration extends SpringHttpSessionConfiguration
implements BeanClassLoaderAware, ImportAware, EmbeddedValueResolverAware {
implements BeanClassLoaderAware, EmbeddedValueResolverAware, ImportAware,
SchedulingConfigurer {
private String tableName;
static final String DEFAULT_CLEANUP_CRON = "0 * * * * *";
private Integer maxInactiveIntervalInSeconds;
private String tableName = JdbcOperationsSessionRepository.DEFAULT_TABLE_NAME;
private Integer maxInactiveIntervalInSeconds = MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS;
private String cleanupCron = DEFAULT_CLEANUP_CRON;
private DataSource dataSource;
private PlatformTransactionManager transactionManager;
private LobHandler lobHandler;
@Autowired(required = false)
@Qualifier("conversionService")
private ConversionService conversionService;
private ConversionService springSessionConversionService;
private ConversionService conversionService;
private ClassLoader classLoader;
private StringValueResolver embeddedValueResolver;
@Bean
public JdbcOperationsSessionRepository sessionRepository(
@SpringSessionDataSource ObjectProvider<DataSource> springSessionDataSource,
ObjectProvider<DataSource> dataSource,
PlatformTransactionManager transactionManager) {
DataSource dataSourceToUse = springSessionDataSource.getIfAvailable();
if (dataSourceToUse == null) {
dataSourceToUse = dataSource.getObject();
}
public JdbcOperationsSessionRepository sessionRepository() {
JdbcOperationsSessionRepository sessionRepository = new JdbcOperationsSessionRepository(
dataSourceToUse, transactionManager);
String tableName = getTableName();
if (StringUtils.hasText(tableName)) {
sessionRepository.setTableName(tableName);
this.dataSource, this.transactionManager);
if (StringUtils.hasText(this.tableName)) {
sessionRepository.setTableName(this.tableName);
}
sessionRepository
.setDefaultMaxInactiveInterval(this.maxInactiveIntervalInSeconds);
@@ -104,35 +105,38 @@ public class JdbcHttpSessionConfiguration extends SpringHttpSessionConfiguration
sessionRepository.setConversionService(this.conversionService);
}
else {
GenericConversionService conversionService = createConversionServiceWithBeanClassLoader();
sessionRepository.setConversionService(conversionService);
sessionRepository
.setConversionService(createConversionServiceWithBeanClassLoader());
}
return sessionRepository;
}
/**
* This must be a separate method because some ClassLoaders load the entire method
* definition even if an if statement guards against it loading. This means that older
* versions of Spring would cause a NoSuchMethodError if this were defined in
* {@link #sessionRepository(ObjectProvider, ObjectProvider, PlatformTransactionManager)}.
*
* @return the default {@link ConversionService}
*/
private GenericConversionService createConversionServiceWithBeanClassLoader() {
GenericConversionService conversionService = new GenericConversionService();
conversionService.addConverter(Object.class, byte[].class,
new SerializingConverter());
conversionService.addConverter(byte[].class, Object.class,
new DeserializingConverter(this.classLoader));
return conversionService;
public void setTableName(String tableName) {
this.tableName = tableName;
}
/* (non-Javadoc)
* @see org.springframework.beans.factory.BeanClassLoaderAware#setBeanClassLoader(java.lang.ClassLoader)
*/
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
public void setMaxInactiveIntervalInSeconds(Integer maxInactiveIntervalInSeconds) {
this.maxInactiveIntervalInSeconds = maxInactiveIntervalInSeconds;
}
public void setCleanupCron(String cleanupCron) {
this.cleanupCron = cleanupCron;
}
@Autowired
public void setDataSource(
@SpringSessionDataSource ObjectProvider<DataSource> springSessionDataSource,
ObjectProvider<DataSource> dataSource) {
DataSource dataSourceToUse = springSessionDataSource.getIfAvailable();
if (dataSourceToUse == null) {
dataSourceToUse = dataSource.getObject();
}
this.dataSource = dataSourceToUse;
}
@Autowired
public void setTransactionManager(PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
@Autowired(required = false)
@@ -147,34 +151,15 @@ public class JdbcHttpSessionConfiguration extends SpringHttpSessionConfiguration
this.springSessionConversionService = conversionService;
}
public void setTableName(String tableName) {
this.tableName = tableName;
}
public void setMaxInactiveIntervalInSeconds(Integer maxInactiveIntervalInSeconds) {
this.maxInactiveIntervalInSeconds = maxInactiveIntervalInSeconds;
}
private String getTableName() {
String systemProperty = System.getProperty("spring.session.jdbc.tableName", "");
if (StringUtils.hasText(systemProperty)) {
return systemProperty;
}
return this.tableName;
@Autowired(required = false)
@Qualifier("conversionService")
public void setConversionService(ConversionService conversionService) {
this.conversionService = conversionService;
}
@Override
public void setImportMetadata(AnnotationMetadata importMetadata) {
Map<String, Object> enableAttrMap = importMetadata
.getAnnotationAttributes(EnableJdbcHttpSession.class.getName());
AnnotationAttributes enableAttrs = AnnotationAttributes.fromMap(enableAttrMap);
String tableNameValue = enableAttrs.getString("tableName");
if (StringUtils.hasText(tableNameValue)) {
this.tableName = this.embeddedValueResolver
.resolveStringValue(tableNameValue);
}
this.maxInactiveIntervalInSeconds = enableAttrs
.getNumber("maxInactiveIntervalInSeconds");
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
@Override
@@ -182,13 +167,37 @@ public class JdbcHttpSessionConfiguration extends SpringHttpSessionConfiguration
this.embeddedValueResolver = resolver;
}
/**
* Property placeholder to process the @Scheduled annotation.
* @return the {@link PropertySourcesPlaceholderConfigurer} to use
*/
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
@Override
public void setImportMetadata(AnnotationMetadata importMetadata) {
Map<String, Object> attributeMap = importMetadata
.getAnnotationAttributes(EnableJdbcHttpSession.class.getName());
AnnotationAttributes attributes = AnnotationAttributes.fromMap(attributeMap);
String tableNameValue = attributes.getString("tableName");
if (StringUtils.hasText(tableNameValue)) {
this.tableName = this.embeddedValueResolver
.resolveStringValue(tableNameValue);
}
this.maxInactiveIntervalInSeconds = attributes
.getNumber("maxInactiveIntervalInSeconds");
String cleanupCron = attributes.getString("cleanupCron");
if (StringUtils.hasText(cleanupCron)) {
this.cleanupCron = this.embeddedValueResolver.resolveStringValue(cleanupCron);
}
}
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.addCronTask(() -> sessionRepository().cleanUpExpiredSessions(),
this.cleanupCron);
}
private GenericConversionService createConversionServiceWithBeanClassLoader() {
GenericConversionService conversionService = new GenericConversionService();
conversionService.addConverter(Object.class, byte[].class,
new SerializingConverter());
conversionService.addConverter(byte[].class, Object.class,
new DeserializingConverter(this.classLoader));
return conversionService;
}
}