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.support;
018
019import org.springframework.cache.Cache;
020
021/**
022 * Common base class for {@link Cache} implementations that need to adapt
023 * {@code null} values (and potentially other such special values) before
024 * passing them on to the underlying store.
025 *
026 * <p>Transparently replaces given {@code null} user values with an internal
027 * {@link NullValue#INSTANCE}, if configured to support {@code null} values
028 * (as indicated by {@link #isAllowNullValues()}.
029 *
030 * @author Juergen Hoeller
031 * @since 4.2.2
032 */
033public abstract class AbstractValueAdaptingCache implements Cache {
034
035        private final boolean allowNullValues;
036
037
038        /**
039         * Create an {@code AbstractValueAdaptingCache} with the given setting.
040         * @param allowNullValues whether to allow for {@code null} values
041         */
042        protected AbstractValueAdaptingCache(boolean allowNullValues) {
043                this.allowNullValues = allowNullValues;
044        }
045
046
047        /**
048         * Return whether {@code null} values are allowed in this cache.
049         */
050        public final boolean isAllowNullValues() {
051                return this.allowNullValues;
052        }
053
054        @Override
055        public ValueWrapper get(Object key) {
056                return toValueWrapper(lookup(key));
057        }
058
059        @Override
060        @SuppressWarnings("unchecked")
061        public <T> T get(Object key, Class<T> type) {
062                Object value = fromStoreValue(lookup(key));
063                if (value != null && type != null && !type.isInstance(value)) {
064                        throw new IllegalStateException(
065                                        "Cached value is not of required type [" + type.getName() + "]: " + value);
066                }
067                return (T) value;
068        }
069
070        /**
071         * Perform an actual lookup in the underlying store.
072         * @param key the key whose associated value is to be returned
073         * @return the raw store value for the key, or {@code null} if none
074         */
075        protected abstract Object lookup(Object key);
076
077
078        /**
079         * Convert the given value from the internal store to a user value
080         * returned from the get method (adapting {@code null}).
081         * @param storeValue the store value
082         * @return the value to return to the user
083         */
084        protected Object fromStoreValue(Object storeValue) {
085                if (this.allowNullValues && storeValue == NullValue.INSTANCE) {
086                        return null;
087                }
088                return storeValue;
089        }
090
091        /**
092         * Convert the given user value, as passed into the put method,
093         * to a value in the internal store (adapting {@code null}).
094         * @param userValue the given user value
095         * @return the value to store
096         */
097        protected Object toStoreValue(Object userValue) {
098                if (this.allowNullValues && userValue == null) {
099                        return NullValue.INSTANCE;
100                }
101                return userValue;
102        }
103
104        /**
105         * Wrap the given store value with a {@link SimpleValueWrapper}, also going
106         * through {@link #fromStoreValue} conversion. Useful for {@link #get(Object)}
107         * and {@link #putIfAbsent(Object, Object)} implementations.
108         * @param storeValue the original value
109         * @return the wrapped value
110         */
111        protected Cache.ValueWrapper toValueWrapper(Object storeValue) {
112                return (storeValue != null ? new SimpleValueWrapper(fromStoreValue(storeValue)) : null);
113        }
114
115}