org.alfresco.repo.cache.InMemoryCacheStatistics.java Source code

Java tutorial

Introduction

Here is the source code for org.alfresco.repo.cache.InMemoryCacheStatistics.java

Source

/*
 * #%L
 * Alfresco Repository
 * %%
 * Copyright (C) 2005 - 2016 Alfresco Software Limited
 * %%
 * This file is part of the Alfresco software. 
 * If the software was purchased under a paid Alfresco license, the terms of 
 * the paid license agreement will prevail.  Otherwise, the software is 
 * provided under the following open source license terms:
 * 
 * Alfresco is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Alfresco is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 * #L%
 */
package org.alfresco.repo.cache;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;

import org.alfresco.repo.cache.TransactionStats.OpType;
import org.apache.commons.math3.stat.descriptive.SummaryStatistics;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

/**
 * Simple non-persistent implementation of {@link CacheStatistics}. Statistics
 * are empty at repository startup.
 * 
 * @since 5.0
 * @author Matt Ward
 */
public class InMemoryCacheStatistics implements CacheStatistics, ApplicationContextAware {
    /** Read/Write locks by cache name */
    private final ConcurrentMap<String, ReentrantReadWriteLock> locks = new ConcurrentHashMap<>();
    private Map<String, Map<OpType, OperationStats>> cacheToStatsMap = new HashMap<>();
    private ApplicationContext applicationContext;

    @Override
    public long count(String cacheName, OpType opType) {
        ReadLock readLock = getReadLock(cacheName);
        readLock.lock();
        try {
            Map<OpType, OperationStats> cacheStats = cacheToStatsMap.get(cacheName);
            if (cacheStats == null) {
                throw new NoStatsForCache(cacheName);
            }
            OperationStats opStats = cacheStats.get(opType);
            return opStats.getCount();
        } finally {
            readLock.unlock();
        }
    }

    @Override
    public double meanTime(String cacheName, OpType opType) {
        ReadLock readLock = getReadLock(cacheName);
        readLock.lock();
        try {
            Map<OpType, OperationStats> cacheStats = cacheToStatsMap.get(cacheName);
            if (cacheStats == null) {
                throw new NoStatsForCache(cacheName);
            }
            OperationStats opStats = cacheStats.get(opType);
            return opStats.meanTime();
        } finally {
            readLock.unlock();
        }
    }

    @Override
    public void add(String cacheName, TransactionStats txStats) {
        boolean registerCacheStats = false;
        WriteLock writeLock = getWriteLock(cacheName);
        writeLock.lock();
        try {
            // Are we adding new stats for a previously unseen cache?
            registerCacheStats = !cacheToStatsMap.containsKey(cacheName);
            if (registerCacheStats) {
                // There are no statistics yet for this cache. 
                cacheToStatsMap.put(cacheName, new HashMap<OpType, OperationStats>());
            }
            Map<OpType, OperationStats> cacheStats = cacheToStatsMap.get(cacheName);

            for (OpType opType : OpType.values()) {
                SummaryStatistics txOpSummary = txStats.getTimings(opType);
                long count = txOpSummary.getN();
                double totalTime = txOpSummary.getSum();

                OperationStats oldStats = cacheStats.get(opType);
                OperationStats newStats;
                if (oldStats == null) {
                    newStats = new OperationStats(totalTime, count);
                } else {
                    newStats = new OperationStats(oldStats, totalTime, count);
                }
                cacheStats.put(opType, newStats);
            }
        } finally {
            writeLock.unlock();
        }

        if (registerCacheStats) {
            // We've added stats for a previously unseen cache, raise an event
            // so that an MBean for the cache may be registered, for example. 
            applicationContext.publishEvent(new CacheStatisticsCreated(this, cacheName));
        }
    }

    @Override
    public double hitMissRatio(String cacheName) {
        ReadLock readLock = getReadLock(cacheName);
        readLock.lock();
        try {
            Map<OpType, OperationStats> cacheStats = cacheToStatsMap.get(cacheName);
            if (cacheStats == null) {
                throw new NoStatsForCache(cacheName);
            }
            long hits = cacheStats.get(OpType.GET_HIT).getCount();
            long misses = cacheStats.get(OpType.GET_MISS).getCount();
            return (double) hits / (hits + misses);
        } finally {
            readLock.unlock();
        }
    }

    @Override
    public long numGets(String cacheName) {
        ReadLock readLock = getReadLock(cacheName);
        readLock.lock();
        try {
            Map<OpType, OperationStats> cacheStats = cacheToStatsMap.get(cacheName);
            if (cacheStats == null) {
                throw new NoStatsForCache(cacheName);
            }
            long hits = cacheStats.get(OpType.GET_HIT).getCount();
            long misses = cacheStats.get(OpType.GET_MISS).getCount();
            return hits + misses;
        } finally {
            readLock.unlock();
        }
    }

    @Override
    public Map<OpType, OperationStats> allStats(String cacheName) {
        ReadLock readLock = getReadLock(cacheName);
        readLock.lock();
        try {
            Map<OpType, OperationStats> cacheStats = cacheToStatsMap.get(cacheName);
            if (cacheStats == null) {
                throw new NoStatsForCache(cacheName);
            }
            return new HashMap<>(cacheStats);
        } finally {
            readLock.unlock();
        }
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    /**
     * Gets a {@link ReentrantReadWriteLock} for a specific cache, lazily
     * creating the lock if necessary. Locks may be created per cache
     * (rather than hashing to a smaller pool) since the number of
     * caches is not too large.
     * 
     * @param cacheName  Cache name to obtain lock for.
     * @return ReentrantReadWriteLock
     */
    private ReentrantReadWriteLock getLock(String cacheName) {
        if (!locks.containsKey(cacheName)) {
            ReentrantReadWriteLock newLock = new ReentrantReadWriteLock();
            if (locks.putIfAbsent(cacheName, newLock) == null) {
                // Lock was successfully added to map.
                return newLock;
            }
            ;
        }
        return locks.get(cacheName);
    }

    private ReadLock getReadLock(String cacheName) {
        ReadLock readLock = getLock(cacheName).readLock();
        return readLock;
    }

    private WriteLock getWriteLock(String cacheName) {
        WriteLock writeLock = getLock(cacheName).writeLock();
        return writeLock;
    }
}