All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.phantomthief.util.RequestContextCache Maven / Gradle / Ivy

There is a newer version: 1.0.10
Show newest version
/**
 * 
 */
package com.github.phantomthief.util;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import com.google.common.collect.Maps;

/**
 * @author w.vela 
 *
 * @date 2014年4月11日 下午4:20:24
 */
public class RequestContextCache extends RequestContextHolder
        implements IMultiDataAccess {

    private static final String PREFIX = "_c";

    private static final int THREAD_LOCAL_NAME_LENGTH = 2;

    private static final Map> ALL_NAMES = new HashMap<>();

    private String uniqueNameForRequestContext;

    private final String declareLocation;
    private final Set> valueTypes = Collections.synchronizedSet(new HashSet<>());

    private final AtomicLong request = new AtomicLong();
    private final AtomicLong hit = new AtomicLong();
    private final AtomicLong set = new AtomicLong();
    private final AtomicLong remove = new AtomicLong();

    public RequestContextCache() {
        synchronized (RequestContextCache.class) {
            String uniqName;
            do {
                uniqName = PREFIX + RandomStringUtils.randomAlphanumeric(THREAD_LOCAL_NAME_LENGTH);
            } while (ALL_NAMES.containsKey(uniqName));
            ALL_NAMES.put(uniqName, this);
            uniqueNameForRequestContext = uniqName;
        }
        String location;
        try {
            location = Thread.currentThread().getStackTrace()[locationCallStackDepth()].toString();
        } catch (Throwable e) {
            location = null;
        }
        declareLocation = location;
    }

    protected int locationCallStackDepth() {
        return 2;
    }

    @SuppressWarnings("unchecked")
    private final Map init() {
        try {
            RequestAttributes attrs = currentRequestAttributes();
            ConcurrentHashMap concurrentHashMap = (ConcurrentHashMap) attrs
                    .getAttribute(uniqueNameForRequestContext, RequestAttributes.SCOPE_REQUEST);
            if (concurrentHashMap == null) {
                synchronized (attrs) {
                    concurrentHashMap = (ConcurrentHashMap) attrs.getAttribute(
                            uniqueNameForRequestContext, RequestAttributes.SCOPE_REQUEST);
                    if (concurrentHashMap == null) {
                        concurrentHashMap = new ConcurrentHashMap<>();
                        attrs.setAttribute(uniqueNameForRequestContext, concurrentHashMap,
                                RequestAttributes.SCOPE_REQUEST);
                    }
                }
            }
            return concurrentHashMap;
        } catch (IllegalStateException e) {
            return null;
        }
    }

    /**
     * 请尽量使用
     * {@link com.github.phantomthief.util.RequestContextCache#get(Collection)}
     * 
     * @param key
     * @return
     */
    public V get(K key) {
        Map thisCache;
        request.addAndGet(1);
        if ((thisCache = init()) != null) {
            V v = thisCache.get(key);
            if (v != null) {
                hit.addAndGet(1);
            }
            return v;
        } else {
            return null;
        }
    }

    @Override
    public Map get(Collection keys) {
        Map thisCache;
        request.addAndGet(CollectionUtils.size(keys));
        if (CollectionUtils.isNotEmpty(keys) && (thisCache = init()) != null) {
            Map map = Collections.unmodifiableMap(Maps.filterKeys(thisCache, keys::contains));
            hit.addAndGet(map.size());
            return map;
        } else {
            return Collections.emptyMap();
        }
    }

    @Override
    public void set(Map dataMap) {
        Map thisMap = init();
        if (thisMap != null) {
            if (MapUtils.isNotEmpty(dataMap)) {
                set.addAndGet(CollectionUtils.size(dataMap));
                thisMap.putAll(dataMap);
                valueTypes.addAll(dataMap.values().stream().filter(Objects::nonNull)
                        .map(Object::getClass).distinct().collect(Collectors.toList()));
            }
        }
    }

    /**
     * 请尽量使用
     * {@link com.github.phantomthief.util.RequestContextCache#set(Map)}
     * 
     * @param key
     * @param value
     */
    public void set(K key, V value) {
        Map thisMap = init();
        if (thisMap != null) {
            if (key != null && value != null) {
                set.addAndGet(1);
                thisMap.put(key, value);
                valueTypes.add(value.getClass());
            }
        }
    }

    public void remove(K key) {
        Map thisMap = init();
        if (thisMap != null) {
            remove.incrementAndGet();
            thisMap.remove(key);
        }
    }

    public final class CacheStats {

        public double getHitRate() {
            return (double) getHitCount() / getRequestCount();
        }

        public long getHitCount() {
            return hit.get();
        }

        public long getRequestCount() {
            return request.get();
        }

        public long getSetCount() {
            return set.get();
        }

        public long getRemoveCount() {
            return remove.get();
        }

        public String getDeclareLocation() {
            return declareLocation;
        }

        public Collection> getValueTypes() {
            return valueTypes;
        }
    }

    public static final List.CacheStats> getStats() {
        synchronized (RequestContextCache.class) {
            return ALL_NAMES.values().stream().map(i -> i.new CacheStats())
                    .collect(Collectors.toList());
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy