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.transaction.support.TransactionSynchronizationAdapter;
023import org.springframework.transaction.support.TransactionSynchronizationManager;
024import org.springframework.util.Assert;
025
026/**
027 * Cache decorator which synchronizes its {@link #put}, {@link #evict} and
028 * {@link #clear} operations with Spring-managed transactions (through Spring's
029 * {@link TransactionSynchronizationManager}, performing the actual cache
030 * put/evict/clear operation only in the after-commit phase of a successful
031 * transaction. If no transaction is active, {@link #put}, {@link #evict} and
032 * {@link #clear} operations will be performed immediately, as usual.
033 *
034 * <p><b>Note:</b> Use of immediate operations such as {@link #putIfAbsent}
035 * cannot be deferred to the after-commit phase of a running transaction.
036 * Use these with care in a transactional environment.
037 *
038 * @author Juergen Hoeller
039 * @author Stephane Nicoll
040 * @author Stas Volsky
041 * @since 3.2
042 * @see TransactionAwareCacheManagerProxy
043 */
044public class TransactionAwareCacheDecorator implements Cache {
045
046        private final Cache targetCache;
047
048
049        /**
050         * Create a new TransactionAwareCache for the given target Cache.
051         * @param targetCache the target Cache to decorate
052         */
053        public TransactionAwareCacheDecorator(Cache targetCache) {
054                Assert.notNull(targetCache, "Target Cache must not be null");
055                this.targetCache = targetCache;
056        }
057
058
059        /**
060         * Return the target Cache that this Cache should delegate to.
061         */
062        public Cache getTargetCache() {
063                return this.targetCache;
064        }
065
066        @Override
067        public String getName() {
068                return this.targetCache.getName();
069        }
070
071        @Override
072        public Object getNativeCache() {
073                return this.targetCache.getNativeCache();
074        }
075
076        @Override
077        public ValueWrapper get(Object key) {
078                return this.targetCache.get(key);
079        }
080
081        @Override
082        public <T> T get(Object key, Class<T> type) {
083                return this.targetCache.get(key, type);
084        }
085
086        @Override
087        public <T> T get(Object key, Callable<T> valueLoader) {
088                return this.targetCache.get(key, valueLoader);
089        }
090
091        @Override
092        public void put(final Object key, final Object value) {
093                if (TransactionSynchronizationManager.isSynchronizationActive()) {
094                        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
095                                @Override
096                                public void afterCommit() {
097                                        TransactionAwareCacheDecorator.this.targetCache.put(key, value);
098                                }
099                        });
100                }
101                else {
102                        this.targetCache.put(key, value);
103                }
104        }
105
106        @Override
107        public ValueWrapper putIfAbsent(Object key, Object value) {
108                return this.targetCache.putIfAbsent(key, value);
109        }
110
111        @Override
112        public void evict(final Object key) {
113                if (TransactionSynchronizationManager.isSynchronizationActive()) {
114                        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
115                                @Override
116                                public void afterCommit() {
117                                        TransactionAwareCacheDecorator.this.targetCache.evict(key);
118                                }
119                        });
120                }
121                else {
122                        this.targetCache.evict(key);
123                }
124        }
125
126        @Override
127        public void clear() {
128                if (TransactionSynchronizationManager.isSynchronizationActive()) {
129                        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
130                                @Override
131                                public void afterCommit() {
132                                        targetCache.clear();
133                                }
134                        });
135                }
136                else {
137                        this.targetCache.clear();
138                }
139        }
140
141}