Skip to content

Commit a8e4659

Browse files
Thomas Darimontchristophstrobl
authored andcommitted
DATAREDIS-375 - Avoid repeatedly decorating a cache.
Previously a transactional RedisCacheManager decorated a cache with the TransactionAwareCacheDecorator twice. We now make sure that a particular cache is only decorated once with a TransactionAwareCacheDecorator. Original Pull Request: spring-projects#128
1 parent c662c46 commit a8e4659

File tree

2 files changed

+144
-0
lines changed

2 files changed

+144
-0
lines changed

src/main/java/org/springframework/data/redis/cache/RedisCacheManager.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,4 +300,22 @@ public void afterPropertiesSet() {
300300

301301
super.afterPropertiesSet();
302302
}
303+
304+
/* (non-Javadoc)
305+
* @see
306+
org.springframework.cache.transaction.AbstractTransactionSupportingCacheManager#decorateCache(org.springframework.cache.Cache)
307+
*/
308+
@Override
309+
protected Cache decorateCache(Cache cache) {
310+
311+
if (isCacheAlreadyDecorated(cache)) {
312+
return cache;
313+
}
314+
315+
return super.decorateCache(cache);
316+
}
317+
318+
protected boolean isCacheAlreadyDecorated(Cache cache) {
319+
return isTransactionAware() && cache instanceof TransactionAwareCacheDecorator;
320+
}
303321
}
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
/*
2+
* Copyright 2015 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+
* http://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+
package org.springframework.data.redis.cache;
17+
18+
import static org.junit.Assert.*;
19+
import static org.mockito.Mockito.*;
20+
21+
import java.sql.Connection;
22+
import java.sql.SQLException;
23+
import java.util.Arrays;
24+
25+
import javax.sql.DataSource;
26+
27+
import org.junit.Test;
28+
import org.junit.runner.RunWith;
29+
import org.springframework.beans.DirectFieldAccessor;
30+
import org.springframework.beans.factory.annotation.Autowired;
31+
import org.springframework.cache.Cache;
32+
import org.springframework.cache.CacheManager;
33+
import org.springframework.cache.annotation.EnableCaching;
34+
import org.springframework.cache.transaction.TransactionAwareCacheDecorator;
35+
import org.springframework.context.annotation.Bean;
36+
import org.springframework.context.annotation.Configuration;
37+
import org.springframework.data.redis.connection.RedisConnection;
38+
import org.springframework.data.redis.connection.RedisConnectionFactory;
39+
import org.springframework.data.redis.core.RedisTemplate;
40+
import org.springframework.data.redis.test.util.RelaxedJUnit4ClassRunner;
41+
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
42+
import org.springframework.test.context.ContextConfiguration;
43+
import org.springframework.test.context.transaction.TransactionConfiguration;
44+
import org.springframework.transaction.PlatformTransactionManager;
45+
import org.springframework.transaction.annotation.Transactional;
46+
47+
/**
48+
* @author Thomas Darimont
49+
*/
50+
@RunWith(RelaxedJUnit4ClassRunner.class)
51+
@ContextConfiguration
52+
@Transactional
53+
@TransactionConfiguration(transactionManager = "transactionManager")
54+
public class RedisCacheManagerTransactionalUnitTests {
55+
56+
@Autowired protected CacheManager cacheManager;
57+
58+
private final static String cacheName = "cache-name";
59+
60+
@Configuration
61+
@EnableCaching
62+
public static class Config {
63+
64+
@Bean
65+
public PlatformTransactionManager transactionManager() throws SQLException {
66+
67+
DataSourceTransactionManager txmgr = new DataSourceTransactionManager();
68+
txmgr.setDataSource(dataSource());
69+
txmgr.afterPropertiesSet();
70+
71+
return txmgr;
72+
}
73+
74+
@Bean
75+
public DataSource dataSource() throws SQLException {
76+
77+
DataSource dataSourceMock = mock(DataSource.class);
78+
when(dataSourceMock.getConnection()).thenReturn(mock(Connection.class));
79+
80+
return dataSourceMock;
81+
}
82+
83+
@Bean
84+
public CacheManager cacheManager() {
85+
86+
RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate());
87+
cacheManager.setTransactionAware(true);
88+
cacheManager.setCacheNames(Arrays.asList(cacheName));
89+
return cacheManager;
90+
}
91+
92+
@SuppressWarnings({ "rawtypes" })
93+
@Bean
94+
public RedisTemplate redisTemplate() {
95+
96+
RedisConnection connectionMock = mock(RedisConnection.class);
97+
RedisConnectionFactory factoryMock = mock(RedisConnectionFactory.class);
98+
99+
when(factoryMock.getConnection()).thenReturn(connectionMock);
100+
101+
RedisTemplate template = new RedisTemplate();
102+
template.setConnectionFactory(factoryMock);
103+
104+
return template;
105+
}
106+
}
107+
108+
/**
109+
* @see DATAREDIS-375
110+
*/
111+
@Test
112+
public void testCacheIsNotDecoratedTwiceWithTransactionAwareCacheDecorator() {
113+
114+
Cache cache = cacheManager.getCache(cacheName);
115+
116+
// the cache must be decorated with the TransactionAwareCacheDecorator
117+
assertTrue(cache instanceof TransactionAwareCacheDecorator);
118+
TransactionAwareCacheDecorator transactionalCache = (TransactionAwareCacheDecorator) cache;
119+
120+
// get the target cache via reflection
121+
Object targetCache = new DirectFieldAccessor(transactionalCache).getPropertyValue("targetCache");
122+
123+
// the target cache must be an instance of RedisCache and not TransactionAwareCacheDecorator
124+
assertTrue(targetCache instanceof RedisCache);
125+
}
126+
}

0 commit comments

Comments
 (0)