package com.elab.redis.interceptor.impl;

import com.elab.redis.annotation.CacheReadLock;
import com.elab.redis.annotation.CacheWriteLock;
import com.elab.redis.interceptor.ICacheProcessService;
import com.elab.redis.utils.CacheParseUtil;
import org.redisson.api.RLock;
import org.redisson.api.RReadWriteLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.ReflectiveMethodInvocation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationUtils;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * 读写锁缓存
 *
 * @author ： liukx
 * @time ： 2020/7/27 - 15:50
 */
public class CacheReadProcessImpl implements ICacheProcessService {

    private Logger logger = LoggerFactory.getLogger(CacheReadProcessImpl.class);

    @Autowired
    private RedissonClient client;

    @Override
    public boolean subscribe(Method method) {
        return CacheParseUtil.isContainAnnotation(method, CacheReadLock.class) || CacheParseUtil.isContainAnnotation(method, CacheWriteLock.class);
    }

    @Override
    public Object invokeWithinTransaction(ReflectiveMethodInvocation invocation) throws Throwable {
        Method method = invocation.getMethod();
        CacheReadLock cacheReadLock = AnnotationUtils.getAnnotation(method, CacheReadLock.class);
        String[] unionKey = cacheReadLock.unionKey();
        Object[] arguments = invocation.getArguments();
        String clazzName = method.getDeclaringClass().getName();
        int timeOut = cacheReadLock.timeOut();
        String cacheKey = CacheParseUtil.generateUnionKey(unionKey, arguments);
        String lockKey =  clazzName + ":" + method.getName();

        RReadWriteLock readWriteLock = client.getReadWriteLock(lockKey);
        RLock readLock = readWriteLock.readLock();
        try {
            if (readLock.tryLock(timeOut, TimeUnit.SECONDS)) {
                Object proceed = invocation.proceed();
                return proceed;
            }
        } catch (InterruptedException e) {
            logger.warn("尝试获取锁超时", e);
        } finally {
            readLock.unlock();
        }
        return null;
    }
}
