001/*
002 * Copyright 2002-2020 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.jcache;
018
019import java.util.concurrent.Callable;
020import javax.cache.Cache;
021import javax.cache.processor.EntryProcessor;
022import javax.cache.processor.EntryProcessorException;
023import javax.cache.processor.MutableEntry;
024
025import org.springframework.cache.support.AbstractValueAdaptingCache;
026import org.springframework.util.Assert;
027
028/**
029 * {@link org.springframework.cache.Cache} implementation on top of a
030 * {@link Cache javax.cache.Cache} instance.
031 *
032 * <p>Note: This class has been updated for JCache 1.0, as of Spring 4.0.
033 *
034 * @author Juergen Hoeller
035 * @author Stephane Nicoll
036 * @since 3.2
037 * @see JCacheCacheManager
038 */
039public class JCacheCache extends AbstractValueAdaptingCache {
040
041        private final Cache<Object, Object> cache;
042
043
044        /**
045         * Create a {@code JCacheCache} instance.
046         * @param jcache backing JCache Cache instance
047         */
048        public JCacheCache(Cache<Object, Object> jcache) {
049                this(jcache, true);
050        }
051
052        /**
053         * Create a {@code JCacheCache} instance.
054         * @param jcache backing JCache Cache instance
055         * @param allowNullValues whether to accept and convert null values for this cache
056         */
057        public JCacheCache(Cache<Object, Object> jcache, boolean allowNullValues) {
058                super(allowNullValues);
059                Assert.notNull(jcache, "Cache must not be null");
060                this.cache = jcache;
061        }
062
063
064        @Override
065        public final String getName() {
066                return this.cache.getName();
067        }
068
069        @Override
070        public final Cache<Object, Object> getNativeCache() {
071                return this.cache;
072        }
073
074        @Override
075        protected Object lookup(Object key) {
076                return this.cache.get(key);
077        }
078
079        @Override
080        public <T> T get(Object key, Callable<T> valueLoader) {
081                try {
082                        return this.cache.invoke(key, new ValueLoaderEntryProcessor<T>(), valueLoader);
083                }
084                catch (EntryProcessorException ex) {
085                        throw new ValueRetrievalException(key, valueLoader, ex.getCause());
086                }
087        }
088
089        @Override
090        public void put(Object key, Object value) {
091                this.cache.put(key, toStoreValue(value));
092        }
093
094        @Override
095        public ValueWrapper putIfAbsent(Object key, Object value) {
096                boolean set = this.cache.putIfAbsent(key, toStoreValue(value));
097                return (set ? null : get(key));
098        }
099
100        @Override
101        public void evict(Object key) {
102                this.cache.remove(key);
103        }
104
105        @Override
106        public void clear() {
107                this.cache.removeAll();
108        }
109
110
111        private class ValueLoaderEntryProcessor<T> implements EntryProcessor<Object, Object, T> {
112
113                @SuppressWarnings("unchecked")
114                @Override
115                public T process(MutableEntry<Object, Object> entry, Object... arguments) throws EntryProcessorException {
116                        Callable<T> valueLoader = (Callable<T>) arguments[0];
117                        if (entry.exists()) {
118                                return (T) fromStoreValue(entry.getValue());
119                        }
120                        else {
121                                T value;
122                                try {
123                                        value = valueLoader.call();
124                                }
125                                catch (Exception ex) {
126                                        throw new EntryProcessorException("Value loader '" + valueLoader + "' failed " +
127                                                        "to compute value for key '" + entry.getKey() + "'", ex);
128                                }
129                                entry.setValue(toStoreValue(value));
130                                return value;
131                        }
132                }
133        }
134
135}