Merge branch '3.2.x'
Closes gh-2817
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2014-2024 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.jdbc.config.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.session.jdbc.JdbcIndexedSessionRepository;
|
||||
|
||||
/**
|
||||
* Qualifier annotation for a
|
||||
* {@link org.springframework.transaction.PlatformTransactionManager} to be injected in
|
||||
* {@link JdbcIndexedSessionRepository}.
|
||||
*
|
||||
* @author Marcus da Coregio
|
||||
* @since 3.1.5
|
||||
*/
|
||||
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Qualifier
|
||||
public @interface SpringSessionTransactionManager {
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
* Copyright 2014-2024 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.
|
||||
@@ -24,10 +24,14 @@ import java.util.stream.Collectors;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
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.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.EmbeddedValueResolverAware;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -56,6 +60,7 @@ import org.springframework.session.config.SessionRepositoryCustomizer;
|
||||
import org.springframework.session.config.annotation.web.http.SpringHttpSessionConfiguration;
|
||||
import org.springframework.session.jdbc.JdbcIndexedSessionRepository;
|
||||
import org.springframework.session.jdbc.config.annotation.SpringSessionDataSource;
|
||||
import org.springframework.session.jdbc.config.annotation.SpringSessionTransactionManager;
|
||||
import org.springframework.session.web.http.SessionRepositoryFilter;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionDefinition;
|
||||
@@ -79,7 +84,8 @@ import org.springframework.util.StringValueResolver;
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Import(SpringHttpSessionConfiguration.class)
|
||||
public class JdbcHttpSessionConfiguration implements BeanClassLoaderAware, EmbeddedValueResolverAware, ImportAware {
|
||||
public class JdbcHttpSessionConfiguration implements BeanClassLoaderAware, EmbeddedValueResolverAware, ImportAware,
|
||||
ApplicationContextAware, InitializingBean {
|
||||
|
||||
private Duration maxInactiveInterval = MapSession.DEFAULT_MAX_INACTIVE_INTERVAL;
|
||||
|
||||
@@ -113,6 +119,21 @@ public class JdbcHttpSessionConfiguration implements BeanClassLoaderAware, Embed
|
||||
|
||||
private SessionIdGenerator sessionIdGenerator = UuidSessionIdGenerator.getInstance();
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
if (this.transactionOperations == null && this.transactionManager == null) {
|
||||
this.transactionManager = getUniqueTransactionManager();
|
||||
if (this.transactionManager == null) {
|
||||
throw new IllegalStateException(
|
||||
"""
|
||||
Could not resolve an unique PlatformTransactionManager bean from the application context.
|
||||
Please provide either a TransactionOperations bean named springSessionTransactionOperations or a PlatformTransactionManager bean qualified with @SpringSessionTransactionManager""");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
public JdbcIndexedSessionRepository sessionRepository() {
|
||||
JdbcTemplate jdbcTemplate = createJdbcTemplate(this.dataSource);
|
||||
@@ -200,7 +221,8 @@ public class JdbcHttpSessionConfiguration implements BeanClassLoaderAware, Embed
|
||||
this.dataSource = dataSourceToUse;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
@Autowired(required = false)
|
||||
@SpringSessionTransactionManager
|
||||
public void setTransactionManager(PlatformTransactionManager transactionManager) {
|
||||
this.transactionManager = transactionManager;
|
||||
}
|
||||
@@ -276,6 +298,15 @@ public class JdbcHttpSessionConfiguration implements BeanClassLoaderAware, Embed
|
||||
this.saveMode = attributes.getEnum("saveMode");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
private PlatformTransactionManager getUniqueTransactionManager() {
|
||||
return this.applicationContext.getBeanProvider(PlatformTransactionManager.class).getIfUnique();
|
||||
}
|
||||
|
||||
private static JdbcTemplate createJdbcTemplate(DataSource dataSource) {
|
||||
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
|
||||
jdbcTemplate.setExceptionTranslator(new SQLErrorCodeSQLExceptionTranslator(dataSource));
|
||||
@@ -283,7 +314,7 @@ public class JdbcHttpSessionConfiguration implements BeanClassLoaderAware, Embed
|
||||
return jdbcTemplate;
|
||||
}
|
||||
|
||||
private static TransactionTemplate createTransactionTemplate(PlatformTransactionManager transactionManager) {
|
||||
private TransactionTemplate createTransactionTemplate(PlatformTransactionManager transactionManager) {
|
||||
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
|
||||
transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
|
||||
transactionTemplate.afterPropertiesSet();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2023 the original author or authors.
|
||||
* Copyright 2014-2024 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.
|
||||
@@ -49,13 +49,19 @@ import org.springframework.session.config.SessionRepositoryCustomizer;
|
||||
import org.springframework.session.jdbc.FixedSessionIdGenerator;
|
||||
import org.springframework.session.jdbc.JdbcIndexedSessionRepository;
|
||||
import org.springframework.session.jdbc.config.annotation.SpringSessionDataSource;
|
||||
import org.springframework.session.jdbc.config.annotation.SpringSessionTransactionManager;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionDefinition;
|
||||
import org.springframework.transaction.TransactionException;
|
||||
import org.springframework.transaction.TransactionManager;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.support.TransactionOperations;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatException;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.assertThatNoException;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
@@ -338,6 +344,34 @@ class JdbcHttpSessionConfigurationTests {
|
||||
assertThat(sessionIdGenerator).isInstanceOf(UuidSessionIdGenerator.class);
|
||||
}
|
||||
|
||||
// gh-2801
|
||||
@Test
|
||||
void configureWhenMultipleTransactionManagersAndQualifiedTransactionOperationsThenApplicationShouldStart() {
|
||||
assertThatNoException().isThrownBy(() -> registerAndRefresh(MultipleTransactionManagerConfig.class,
|
||||
CustomTransactionOperationsConfiguration.class));
|
||||
}
|
||||
|
||||
// gh-2801
|
||||
@Test
|
||||
void configureWhenMultipleTransactionManagersAndQualifiedTransactionManagerThenApplicationShouldStartAndUseQualified() {
|
||||
assertThatNoException().isThrownBy(() -> registerAndRefresh(MultipleTransactionManagerConfig.class,
|
||||
QualifiedTransactionManagerConfig.class, DefaultConfiguration.class));
|
||||
JdbcHttpSessionConfiguration configuration = this.context.getBean(JdbcHttpSessionConfiguration.class);
|
||||
Object transactionManager = ReflectionTestUtils.getField(configuration, "transactionManager");
|
||||
assertThat(transactionManager).isInstanceOf(MyTransactionManager.class);
|
||||
}
|
||||
|
||||
// gh-2801
|
||||
@Test
|
||||
void configureWhenMultipleTransactionManagersAndNoQualifiedBeanThenApplicationShouldFailToStart() {
|
||||
assertThatException()
|
||||
.isThrownBy(() -> registerAndRefresh(MultipleTransactionManagerConfig.class, DefaultConfiguration.class))
|
||||
.havingRootCause()
|
||||
.isInstanceOf(IllegalStateException.class)
|
||||
.withMessage("Could not resolve an unique PlatformTransactionManager bean from the application context.\n"
|
||||
+ "Please provide either a TransactionOperations bean named springSessionTransactionOperations or a PlatformTransactionManager bean qualified with @SpringSessionTransactionManager");
|
||||
}
|
||||
|
||||
private void registerAndRefresh(Class<?>... annotatedClasses) {
|
||||
this.context.register(annotatedClasses);
|
||||
this.context.refresh();
|
||||
@@ -606,4 +640,59 @@ class JdbcHttpSessionConfigurationTests {
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class MultipleTransactionManagerConfig {
|
||||
|
||||
@Bean
|
||||
DataSource dataSource() {
|
||||
return mock(DataSource.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
TransactionManager transactionManager1() {
|
||||
return new MyTransactionManager();
|
||||
}
|
||||
|
||||
@Bean
|
||||
TransactionManager transactionManager2() {
|
||||
return mock(PlatformTransactionManager.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class QualifiedTransactionManagerConfig {
|
||||
|
||||
@Bean
|
||||
DataSource dataSource() {
|
||||
return mock(DataSource.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@SpringSessionTransactionManager
|
||||
TransactionManager myTransactionManager() {
|
||||
return new MyTransactionManager();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class MyTransactionManager implements PlatformTransactionManager {
|
||||
|
||||
@Override
|
||||
public TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commit(TransactionStatus status) throws TransactionException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rollback(TransactionStatus status) throws TransactionException {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user