org.redis.cache.RedisCacheManager.java Source code

Java tutorial

Introduction

Here is the source code for org.redis.cache.RedisCacheManager.java

Source

/*
 * Copyright 2011-2015 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.redis.cache;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.redis.cache.authentication.AuthenticationSession;
import org.redis.cache.authentication.AuthenticationToken;
import org.springframework.cache.Cache;
import org.springframework.cache.transaction.AbstractTransactionSupportingCacheManager;
import org.springframework.cache.transaction.TransactionAwareCacheDecorator;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.cache.DefaultRedisCachePrefix;
import org.springframework.data.redis.cache.RedisCachePrefix;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

/**
 * @author <font color="red"><b>Liu.Gang.Qiang</b></font>
 * @Date 20161028
 * @Version 1.0
 * @Description RedisCacheManager
 */
public class RedisCacheManager extends AbstractTransactionSupportingCacheManager {

    private final Log logger = LogFactory.getLog(RedisCacheManager.class);

    @SuppressWarnings("rawtypes") //
    private final RedisOperations redisOperations;

    private boolean usePrefix = false;
    private RedisCachePrefix cachePrefix = new DefaultRedisCachePrefix();
    private boolean loadRemoteCachesOnStartup = false;
    private boolean dynamic = true;

    // 0 - never expire
    private long defaultExpiration = 0;
    private Map<String, Long> expires = null;

    private Set<String> configuredCacheNames;

    /**
     * @author <font color="green"><b>Liu.Gang.Qiang</b></font>
     * @param authenticationToken
     * @date 2016112
     * @version 1.0
     * @description ?
     */
    public void AuthenticationToken(AuthenticationToken authenticationToken) {
        this.getCache(authenticationToken.getCacheName()).put(authenticationToken.getKey(),
                authenticationToken.getSession());
    }

    /**
     * @author <font color="green"><b>Liu.Gang.Qiang</b></font>
     * @param authenticationToken
     * @return {@link AuthenticationSession}
     * @date 2016112
     * @version 1.0
     * @description ?
     */
    public AuthenticationSession getSession(AuthenticationToken authenticationToken) {
        String cacheName = authenticationToken.getCacheName();
        if (cacheName == null || cacheName == "") {
            throw new RuntimeException("redis cache name is empty");
        }
        // key
        String cacheKey = authenticationToken.getKey();
        if (cacheKey == null || cacheKey.length() <= 32) {
            throw new RuntimeException("login token identify length lacks");
        }
        Cache cache = this.getCache(cacheName);
        if (cache != null) {
            AuthenticationSession loginToken = cache.get(cacheKey.substring(32), AuthenticationSession.class);
            if (loginToken != null) {
                return cacheKey.equals(loginToken.getIdentify()) ? loginToken : null;
            }
        }
        return null;
    }

    /**
     * Construct a {@link RedisCacheManager}.
     * 
     * @param redisOperations
     */
    @SuppressWarnings("rawtypes")
    public RedisCacheManager(RedisOperations redisOperations) {
        this(redisOperations, Collections.<String>emptyList());
    }

    /**
     * Construct a static {@link RedisCacheManager}, managing caches for the
     * specified cache names only.
     * 
     * @param redisOperations
     * @param cacheNames
     * @since 1.2
     */
    @SuppressWarnings("rawtypes")
    public RedisCacheManager(RedisOperations redisOperations, Collection<String> cacheNames) {
        this.redisOperations = redisOperations;
        setCacheNames(cacheNames);
    }

    public Cache getCache(String name) {
        Cache cache = super.getCache(name);
        if (cache == null && this.dynamic) {
            return createAndAddCache(name);
        }
        return cache;
    }

    /**
     * Specify the set of cache names for this CacheManager's 'static' mode.
     * <br>
     * The number of caches and their names will be fixed after a call to this
     * method, with no creation of further cache regions at runtime. <br>
     * Calling this with a {@code null} or empty collection argument resets the
     * mode to 'dynamic', allowing for further creation of caches again.
     */
    public void setCacheNames(Collection<String> cacheNames) {

        Set<String> newCacheNames = CollectionUtils.isEmpty(cacheNames) ? Collections.<String>emptySet()
                : new HashSet<String>(cacheNames);

        this.configuredCacheNames = newCacheNames;
        this.dynamic = newCacheNames.isEmpty();
    }

    public void setUsePrefix(boolean usePrefix) {
        this.usePrefix = usePrefix;
    }

    /**
     * Sets the cachePrefix. Defaults to 'DefaultRedisCachePrefix').
     * 
     * @param cachePrefix
     *            the cachePrefix to set
     */
    public void setCachePrefix(RedisCachePrefix cachePrefix) {
        this.cachePrefix = cachePrefix;
    }

    /**
     * Sets the default expire time (in seconds).
     * 
     * @param defaultExpireTime
     *            time in seconds.
     */
    public void setDefaultExpiration(long defaultExpireTime) {
        this.defaultExpiration = defaultExpireTime;
    }

    /**
     * Sets the expire time (in seconds) for cache regions (by key).
     * 
     * @param expires
     *            time in seconds
     */
    public void setExpires(Map<String, Long> expires) {
        this.expires = (expires != null ? new ConcurrentHashMap<String, Long>(expires) : null);
    }

    /**
     * If set to {@code true} {@link RedisCacheManager} will try to retrieve
     * cache names from redis server using {@literal KEYS} command and
     * initialize {@link RedisCache} for each of them.
     * 
     * @param loadRemoteCachesOnStartup
     * @since 1.2
     */
    public void setLoadRemoteCachesOnStartup(boolean loadRemoteCachesOnStartup) {
        this.loadRemoteCachesOnStartup = loadRemoteCachesOnStartup;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.springframework.cache.support.AbstractCacheManager#loadCaches()
     */
    @Override
    protected Collection<? extends Cache> loadCaches() {

        Assert.notNull(this.redisOperations, "A redis template is required in order to interact with data store");
        return addConfiguredCachesIfNecessary(
                loadRemoteCachesOnStartup ? loadAndInitRemoteCaches() : Collections.<Cache>emptyList());
    }

    /**
     * Returns a new {@link Collection} of {@link Cache} from the given caches
     * collection and adds the configured {@link Cache}s of they are not already
     * present.
     * 
     * @param caches
     *            must not be {@literal null}
     * @return
     */
    protected Collection<? extends Cache> addConfiguredCachesIfNecessary(Collection<? extends Cache> caches) {

        Assert.notNull(caches, "Caches must not be null!");

        Collection<Cache> result = new ArrayList<Cache>(caches);

        for (String cacheName : getCacheNames()) {

            boolean configuredCacheAlreadyPresent = false;

            for (Cache cache : caches) {

                if (cache.getName().equals(cacheName)) {
                    configuredCacheAlreadyPresent = true;
                    break;
                }
            }

            if (!configuredCacheAlreadyPresent) {
                result.add(getCache(cacheName));
            }
        }

        return result;
    }

    protected Cache createAndAddCache(String cacheName) {
        addCache(createCache(cacheName));
        return super.getCache(cacheName);
    }

    @SuppressWarnings("unchecked")
    protected RedisCache createCache(String cacheName) {
        long expiration = computeExpiration(cacheName);
        return new RedisCache(cacheName, (usePrefix ? cachePrefix.prefix(cacheName) : null), redisOperations,
                expiration);
    }

    protected long computeExpiration(String name) {
        Long expiration = null;
        if (expires != null) {
            expiration = expires.get(name);
        }
        return (expiration != null ? expiration.longValue() : defaultExpiration);
    }

    protected List<Cache> loadAndInitRemoteCaches() {

        List<Cache> caches = new ArrayList<Cache>();

        try {
            Set<String> cacheNames = loadRemoteCacheKeys();
            if (!CollectionUtils.isEmpty(cacheNames)) {
                for (String cacheName : cacheNames) {
                    if (null == super.getCache(cacheName)) {
                        caches.add(createCache(cacheName));
                    }
                }
            }
        } catch (Exception e) {
            if (logger.isWarnEnabled()) {
                logger.warn("Failed to initialize cache with remote cache keys.", e);
            }
        }

        return caches;
    }

    @SuppressWarnings("unchecked")
    protected Set<String> loadRemoteCacheKeys() {
        return (Set<String>) redisOperations.execute(new RedisCallback<Set<String>>() {

            @Override
            public Set<String> doInRedis(RedisConnection connection) throws DataAccessException {

                // we are using the ~keys postfix as defined in
                // RedisCache#setName
                Set<byte[]> keys = connection.keys(redisOperations.getKeySerializer().serialize("*~keys"));
                Set<String> cacheKeys = new LinkedHashSet<String>();

                if (!CollectionUtils.isEmpty(keys)) {
                    for (byte[] key : keys) {
                        cacheKeys.add(redisOperations.getKeySerializer().deserialize(key).toString()
                                .replace("~keys", ""));
                    }
                }

                return cacheKeys;
            }
        });
    }

    @SuppressWarnings("rawtypes")
    protected RedisOperations getRedisOperations() {
        return redisOperations;
    }

    protected RedisCachePrefix getCachePrefix() {
        return cachePrefix;
    }

    protected boolean isUsePrefix() {
        return usePrefix;
    }

    /**
     * The number of caches and their names will be fixed after a call to this
     * method, with no creation of further cache regions at runtime.
     * 
     * @see org.springframework.cache.support.AbstractCacheManager#afterPropertiesSet()
     */
    @Override
    public void afterPropertiesSet() {

        if (!CollectionUtils.isEmpty(configuredCacheNames)) {

            for (String cacheName : configuredCacheNames) {
                createAndAddCache(cacheName);
            }

            configuredCacheNames.clear();
        }

        super.afterPropertiesSet();
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.springframework.cache.transaction.
     * AbstractTransactionSupportingCacheManager#decorateCache(org.
     * springframework.cache.Cache)
     */
    @Override
    protected Cache decorateCache(Cache cache) {

        if (isCacheAlreadyDecorated(cache)) {
            return cache;
        }

        return super.decorateCache(cache);
    }

    protected boolean isCacheAlreadyDecorated(Cache cache) {
        return isTransactionAware() && cache instanceof TransactionAwareCacheDecorator;
    }
}