Skip to content

Add support for configuring JDBC session cleanup cron #907

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 27, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@
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;
Expand Down Expand Up @@ -535,7 +534,6 @@ public Map<String, JdbcSession> findByIndexNameAndIndexValue(String indexName,
return sessionMap;
}

@Scheduled(cron = "${spring.session.cleanup.cron.expression:0 * * * * *}")
public void cleanUpExpiredSessions() {
Integer deletedCount = this.transactionOperations.execute(transactionStatus ->
JdbcOperationsSessionRepository.this.jdbcOperations.update(
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -58,8 +58,8 @@
* 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
Expand All @@ -80,11 +80,16 @@
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;

}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
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;
Expand All @@ -37,6 +36,9 @@
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;
Expand All @@ -59,38 +61,37 @@
@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 LobHandler lobHandler;
private Integer maxInactiveIntervalInSeconds = MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS;

@Autowired(required = false)
@Qualifier("conversionService")
private ConversionService conversionService;
private String cleanupCron = DEFAULT_CLEANUP_CRON;

private DataSource dataSource;

private PlatformTransactionManager transactionManager;

private LobHandler lobHandler;

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);
Expand All @@ -104,35 +105,38 @@ else if (this.conversionService != null) {
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)
Expand All @@ -147,48 +151,53 @@ public void setSpringSessionConversionService(ConversionService conversionServic
this.springSessionConversionService = conversionService;
}

public void setTableName(String tableName) {
this.tableName = tableName;
@Autowired(required = false)
@Qualifier("conversionService")
public void setConversionService(ConversionService conversionService) {
this.conversionService = conversionService;
}

public void setMaxInactiveIntervalInSeconds(Integer maxInactiveIntervalInSeconds) {
this.maxInactiveIntervalInSeconds = maxInactiveIntervalInSeconds;
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}

private String getTableName() {
String systemProperty = System.getProperty("spring.session.jdbc.tableName", "");
if (StringUtils.hasText(systemProperty)) {
return systemProperty;
}
return this.tableName;
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.embeddedValueResolver = resolver;
}

@Override
public void setImportMetadata(AnnotationMetadata importMetadata) {
Map<String, Object> enableAttrMap = importMetadata
Map<String, Object> attributeMap = importMetadata
.getAnnotationAttributes(EnableJdbcHttpSession.class.getName());
AnnotationAttributes enableAttrs = AnnotationAttributes.fromMap(enableAttrMap);
String tableNameValue = enableAttrs.getString("tableName");
AnnotationAttributes attributes = AnnotationAttributes.fromMap(attributeMap);
String tableNameValue = attributes.getString("tableName");
if (StringUtils.hasText(tableNameValue)) {
this.tableName = this.embeddedValueResolver
.resolveStringValue(tableNameValue);
}
this.maxInactiveIntervalInSeconds = enableAttrs
this.maxInactiveIntervalInSeconds = attributes
.getNumber("maxInactiveIntervalInSeconds");
String cleanupCron = attributes.getString("cleanupCron");
if (StringUtils.hasText(cleanupCron)) {
this.cleanupCron = this.embeddedValueResolver.resolveStringValue(cleanupCron);
}
}

@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.embeddedValueResolver = resolver;
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.addCronTask(() -> sessionRepository().cleanUpExpiredSessions(),
this.cleanupCron);
}

/**
* Property placeholder to process the @Scheduled annotation.
* @return the {@link PropertySourcesPlaceholderConfigurer} to use
*/
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
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;
}

}

This file was deleted.

Loading