Skip to content

Commit 97c3853

Browse files
Merge branch '3.1.x' into 3.2.x
Closes gh-2816
2 parents 5db2ea2 + 4920499 commit 97c3853

File tree

3 files changed

+167
-5
lines changed

3 files changed

+167
-5
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright 2014-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.session.jdbc.config.annotation;
18+
19+
import java.lang.annotation.Documented;
20+
import java.lang.annotation.ElementType;
21+
import java.lang.annotation.Retention;
22+
import java.lang.annotation.RetentionPolicy;
23+
import java.lang.annotation.Target;
24+
25+
import org.springframework.beans.factory.annotation.Qualifier;
26+
import org.springframework.session.jdbc.JdbcIndexedSessionRepository;
27+
28+
/**
29+
* Qualifier annotation for a
30+
* {@link org.springframework.transaction.PlatformTransactionManager} to be injected in
31+
* {@link JdbcIndexedSessionRepository}.
32+
*
33+
* @author Marcus da Coregio
34+
* @since 3.1.5
35+
*/
36+
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE })
37+
@Retention(RetentionPolicy.RUNTIME)
38+
@Documented
39+
@Qualifier
40+
public @interface SpringSessionTransactionManager {
41+
42+
}

spring-session-jdbc/src/main/java/org/springframework/session/jdbc/config/annotation/web/http/JdbcHttpSessionConfiguration.java

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2014-2022 the original author or authors.
2+
* Copyright 2014-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -24,10 +24,14 @@
2424

2525
import javax.sql.DataSource;
2626

27+
import org.springframework.beans.BeansException;
2728
import org.springframework.beans.factory.BeanClassLoaderAware;
29+
import org.springframework.beans.factory.InitializingBean;
2830
import org.springframework.beans.factory.ObjectProvider;
2931
import org.springframework.beans.factory.annotation.Autowired;
3032
import org.springframework.beans.factory.annotation.Qualifier;
33+
import org.springframework.context.ApplicationContext;
34+
import org.springframework.context.ApplicationContextAware;
3135
import org.springframework.context.EmbeddedValueResolverAware;
3236
import org.springframework.context.annotation.Bean;
3337
import org.springframework.context.annotation.Configuration;
@@ -56,6 +60,7 @@
5660
import org.springframework.session.config.annotation.web.http.SpringHttpSessionConfiguration;
5761
import org.springframework.session.jdbc.JdbcIndexedSessionRepository;
5862
import org.springframework.session.jdbc.config.annotation.SpringSessionDataSource;
63+
import org.springframework.session.jdbc.config.annotation.SpringSessionTransactionManager;
5964
import org.springframework.session.web.http.SessionRepositoryFilter;
6065
import org.springframework.transaction.PlatformTransactionManager;
6166
import org.springframework.transaction.TransactionDefinition;
@@ -79,7 +84,8 @@
7984
*/
8085
@Configuration(proxyBeanMethods = false)
8186
@Import(SpringHttpSessionConfiguration.class)
82-
public class JdbcHttpSessionConfiguration implements BeanClassLoaderAware, EmbeddedValueResolverAware, ImportAware {
87+
public class JdbcHttpSessionConfiguration implements BeanClassLoaderAware, EmbeddedValueResolverAware, ImportAware,
88+
ApplicationContextAware, InitializingBean {
8389

8490
private Duration maxInactiveInterval = MapSession.DEFAULT_MAX_INACTIVE_INTERVAL;
8591

@@ -113,6 +119,21 @@ public class JdbcHttpSessionConfiguration implements BeanClassLoaderAware, Embed
113119

114120
private SessionIdGenerator sessionIdGenerator = UuidSessionIdGenerator.getInstance();
115121

122+
private ApplicationContext applicationContext;
123+
124+
@Override
125+
public void afterPropertiesSet() throws Exception {
126+
if (this.transactionOperations == null && this.transactionManager == null) {
127+
this.transactionManager = getUniqueTransactionManager();
128+
if (this.transactionManager == null) {
129+
throw new IllegalStateException(
130+
"""
131+
Could not resolve an unique PlatformTransactionManager bean from the application context.
132+
Please provide either a TransactionOperations bean named springSessionTransactionOperations or a PlatformTransactionManager bean qualified with @SpringSessionTransactionManager""");
133+
}
134+
}
135+
}
136+
116137
@Bean
117138
public JdbcIndexedSessionRepository sessionRepository() {
118139
JdbcTemplate jdbcTemplate = createJdbcTemplate(this.dataSource);
@@ -200,7 +221,8 @@ public void setDataSource(@SpringSessionDataSource ObjectProvider<DataSource> sp
200221
this.dataSource = dataSourceToUse;
201222
}
202223

203-
@Autowired
224+
@Autowired(required = false)
225+
@SpringSessionTransactionManager
204226
public void setTransactionManager(PlatformTransactionManager transactionManager) {
205227
this.transactionManager = transactionManager;
206228
}
@@ -276,14 +298,23 @@ public void setImportMetadata(AnnotationMetadata importMetadata) {
276298
this.saveMode = attributes.getEnum("saveMode");
277299
}
278300

301+
@Override
302+
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
303+
this.applicationContext = applicationContext;
304+
}
305+
306+
private PlatformTransactionManager getUniqueTransactionManager() {
307+
return this.applicationContext.getBeanProvider(PlatformTransactionManager.class).getIfUnique();
308+
}
309+
279310
private static JdbcTemplate createJdbcTemplate(DataSource dataSource) {
280311
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
281312
jdbcTemplate.setExceptionTranslator(new SQLErrorCodeSQLExceptionTranslator(dataSource));
282313
jdbcTemplate.afterPropertiesSet();
283314
return jdbcTemplate;
284315
}
285316

286-
private static TransactionTemplate createTransactionTemplate(PlatformTransactionManager transactionManager) {
317+
private TransactionTemplate createTransactionTemplate(PlatformTransactionManager transactionManager) {
287318
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
288319
transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
289320
transactionTemplate.afterPropertiesSet();

spring-session-jdbc/src/test/java/org/springframework/session/jdbc/config/annotation/web/http/JdbcHttpSessionConfigurationTests.java

Lines changed: 90 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2014-2023 the original author or authors.
2+
* Copyright 2014-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -49,13 +49,19 @@
4949
import org.springframework.session.jdbc.FixedSessionIdGenerator;
5050
import org.springframework.session.jdbc.JdbcIndexedSessionRepository;
5151
import org.springframework.session.jdbc.config.annotation.SpringSessionDataSource;
52+
import org.springframework.session.jdbc.config.annotation.SpringSessionTransactionManager;
5253
import org.springframework.test.util.ReflectionTestUtils;
5354
import org.springframework.transaction.PlatformTransactionManager;
5455
import org.springframework.transaction.TransactionDefinition;
56+
import org.springframework.transaction.TransactionException;
57+
import org.springframework.transaction.TransactionManager;
58+
import org.springframework.transaction.TransactionStatus;
5559
import org.springframework.transaction.support.TransactionOperations;
5660

5761
import static org.assertj.core.api.Assertions.assertThat;
62+
import static org.assertj.core.api.Assertions.assertThatException;
5863
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
64+
import static org.assertj.core.api.Assertions.assertThatNoException;
5965
import static org.mockito.Mockito.mock;
6066

6167
/**
@@ -338,6 +344,34 @@ void sessionIdGeneratorWhenNoBeanThenDefault() {
338344
assertThat(sessionIdGenerator).isInstanceOf(UuidSessionIdGenerator.class);
339345
}
340346

347+
// gh-2801
348+
@Test
349+
void configureWhenMultipleTransactionManagersAndQualifiedTransactionOperationsThenApplicationShouldStart() {
350+
assertThatNoException().isThrownBy(() -> registerAndRefresh(MultipleTransactionManagerConfig.class,
351+
CustomTransactionOperationsConfiguration.class));
352+
}
353+
354+
// gh-2801
355+
@Test
356+
void configureWhenMultipleTransactionManagersAndQualifiedTransactionManagerThenApplicationShouldStartAndUseQualified() {
357+
assertThatNoException().isThrownBy(() -> registerAndRefresh(MultipleTransactionManagerConfig.class,
358+
QualifiedTransactionManagerConfig.class, DefaultConfiguration.class));
359+
JdbcHttpSessionConfiguration configuration = this.context.getBean(JdbcHttpSessionConfiguration.class);
360+
Object transactionManager = ReflectionTestUtils.getField(configuration, "transactionManager");
361+
assertThat(transactionManager).isInstanceOf(MyTransactionManager.class);
362+
}
363+
364+
// gh-2801
365+
@Test
366+
void configureWhenMultipleTransactionManagersAndNoQualifiedBeanThenApplicationShouldFailToStart() {
367+
assertThatException()
368+
.isThrownBy(() -> registerAndRefresh(MultipleTransactionManagerConfig.class, DefaultConfiguration.class))
369+
.havingRootCause()
370+
.isInstanceOf(IllegalStateException.class)
371+
.withMessage("Could not resolve an unique PlatformTransactionManager bean from the application context.\n"
372+
+ "Please provide either a TransactionOperations bean named springSessionTransactionOperations or a PlatformTransactionManager bean qualified with @SpringSessionTransactionManager");
373+
}
374+
341375
private void registerAndRefresh(Class<?>... annotatedClasses) {
342376
this.context.register(annotatedClasses);
343377
this.context.refresh();
@@ -606,4 +640,59 @@ SessionRepositoryCustomizer<JdbcIndexedSessionRepository> sessionRepositoryCusto
606640

607641
}
608642

643+
@Configuration(proxyBeanMethods = false)
644+
static class MultipleTransactionManagerConfig {
645+
646+
@Bean
647+
DataSource dataSource() {
648+
return mock(DataSource.class);
649+
}
650+
651+
@Bean
652+
TransactionManager transactionManager1() {
653+
return new MyTransactionManager();
654+
}
655+
656+
@Bean
657+
TransactionManager transactionManager2() {
658+
return mock(PlatformTransactionManager.class);
659+
}
660+
661+
}
662+
663+
@Configuration(proxyBeanMethods = false)
664+
static class QualifiedTransactionManagerConfig {
665+
666+
@Bean
667+
DataSource dataSource() {
668+
return mock(DataSource.class);
669+
}
670+
671+
@Bean
672+
@SpringSessionTransactionManager
673+
TransactionManager myTransactionManager() {
674+
return new MyTransactionManager();
675+
}
676+
677+
}
678+
679+
static class MyTransactionManager implements PlatformTransactionManager {
680+
681+
@Override
682+
public TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
683+
return null;
684+
}
685+
686+
@Override
687+
public void commit(TransactionStatus status) throws TransactionException {
688+
689+
}
690+
691+
@Override
692+
public void rollback(TransactionStatus status) throws TransactionException {
693+
694+
}
695+
696+
}
697+
609698
}

0 commit comments

Comments
 (0)