001/*
002 * Copyright 2002-2019 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.cache.transaction;
018
019import java.util.concurrent.Callable;
020
021import org.springframework.cache.Cache;
022import org.springframework.lang.Nullable;
023import org.springframework.transaction.support.TransactionSynchronizationAdapter;
024import org.springframework.transaction.support.TransactionSynchronizationManager;
025import org.springframework.util.Assert;
026
027/**
028 * Cache decorator which synchronizes its {@link #put}, {@link #evict} and
029 * {@link #clear} operations with Spring-managed transactions (through Spring's
030 * {@link TransactionSynchronizationManager}, performing the actual cache
031 * put/evict/clear operation only in the after-commit phase of a successful
032 * transaction. If no transaction is active, {@link #put}, {@link #evict} and
033 * {@link #clear} operations will be performed immediately, as usual.
034 *
035 * <p><b>Note:</b> Use of immediate operations such as {@link #putIfAbsent} and
036 * {@link #evictIfPresent} cannot be deferred to the after-commit phase of a
037 * running transaction. Use these with care in a transactional environment.
038 *
039 * @author Juergen Hoeller
040 * @author Stephane Nicoll
041 * @author Stas Volsky
042 * @since 3.2
043 * @see TransactionAwareCacheManagerProxy
044 */
045public class TransactionAwareCacheDecorator implements Cache {
046
047        private final Cache targetCache;
048
049
050        /**
051         * Create a new TransactionAwareCache for the given target Cache.
052         * @param targetCache the target Cache to decorate
053         */
054        public TransactionAwareCacheDecorator(Cache targetCache) {
055                Assert.notNull(targetCache, "Target Cache must not be null");
056                this.targetCache = targetCache;
057        }
058
059
060        /**
061         * Return the target Cache that this Cache should delegate to.
062         */
063        public Cache getTargetCache() {
064                return this.targetCache;
065        }
066
067        @Override
068        public String getName() {
069                return this.targetCache.getName();
070        }
071
072        @Override
073        public Object getNativeCache() {
074                return this.targetCache.getNativeCache();
075        }
076
077        @Override
078        @Nullable
079        public ValueWrapper get(Object key) {
080                return this.targetCache.get(key);
081        }
082
083        @Override
084        public <T> T get(Object key, @Nullable Class<T> type) {
085                return this.targetCache.get(key, type);
086        }
087
088        @Override
089        @Nullable
090        public <T> T get(Object key, Callable<T> valueLoader) {
091                return this.targetCache.get(key, valueLoader);
092        }
093
094        @Override
095        public void put(final Object key, @Nullable final Object value) {
096                if (TransactionSynchronizationManager.isSynchronizationActive()) {
097                        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
098                                @Override
099                                public void afterCommit() {
100                                        TransactionAwareCacheDecorator.this.targetCache.put(key, value);
101                                }
102                        });
103                }
104                else {
105                        this.targetCache.put(key, value);
106                }
107        }
108
109        @Override
110        @Nullable
111        public ValueWrapper putIfAbsent(Object key, @Nullable Object value) {
112                return this.targetCache.putIfAbsent(key, value);
113        }
114
115        @Override
116        public void evict(final Object key) {
117                if (TransactionSynchronizationManager.isSynchronizationActive()) {
118                        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
119                                @Override
120                                public void afterCommit() {
121                                        TransactionAwareCacheDecorator.this.targetCache.evict(key);
122                                }
123                        });
124                }
125                else {
126                        this.targetCache.evict(key);
127                }
128        }
129
130        @Override
131        public boolean evictIfPresent(Object key) {
132                return this.targetCache.evictIfPresent(key);
133        }
134
135        @Override
136        public void clear() {
137                if (TransactionSynchronizationManager.isSynchronizationActive()) {
138                        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
139                                @Override
140                                public void afterCommit() {
141                                        targetCache.clear();
142                                }
143                        });
144                }
145                else {
146                        this.targetCache.clear();
147                }
148        }
149
150        @Override
151        public boolean invalidate() {
152                return this.targetCache.invalidate();
153        }
154
155}